pax_global_header00006660000000000000000000000064145065757000014523gustar00rootroot0000000000000052 comment=103c364ecf67beb83688d60bb113aca9b0acc0e4 sfwbar-1.0~beta13/000077500000000000000000000000001450657570000140455ustar00rootroot00000000000000sfwbar-1.0~beta13/.github/000077500000000000000000000000001450657570000154055ustar00rootroot00000000000000sfwbar-1.0~beta13/.github/sfwbar-dark.png000066400000000000000000000406611450657570000203250ustar00rootroot00000000000000PNG  IHDR|iCCPICC profile(}=H@ߦjT ␡:Yp*BZu0GhҐ8 .κ: ]Rh=/wB4ctL'b6"^F&efGwq_Q" 3Lxxj68GYIVωGL #8]xf̤爣b6f%S# NBcg\e{Fi ",BUl quR,<prmch]?U" q>.Ш94N3p:0IzŎm⺥){dȦJAZB7倾[{[@z"e;޷k'r6a bKGD pHYs  tIME7c!3tEXtCommentCreated with GIMPW IDATxwx?3{HB (П +El(M@AT@EDF!fӷMR6(~'Ovw99{VEAQ ((,<4n:nq2 %#""4wCKnFBE{~]Zn57(H(7p 7p h5j$' @^^?uc٪\۱c 4V{^Ӿ]B?Y!7p 7p 7ȑ#dgu7H=ɓ11>𣋮1<֯_[I( (o,e†]#k׎S_$?s=_Z6y'i۶ V\TPm^L8=jr +gZ9|L -q 7p 7pgҤUHxdk&0$ ZSm:n#/+Stl\,y%KUK=pu4*\:SX )SղvGNmȑHCj+ܐn{"44 给سg/999q~!U<̞=[+a2cKӱZ-L0 iSy| |s.;w…ɓ8q"f;&)-~ AЀA\}rZ_ЍmϹwn%e*v? 00 _׷t*HB""=c~&/''}[5{oGRUo56vo7U)_Q*k͛DqkPPvRˮ^AHZW֭a@@?GrϘ11(Bzz:GB5ϟF-700#cAj|7mڔMңGƍokYd&N|;#qssNV+V_#s3wlrssy4mڔΝ;#1YI&v: 6|ηnDeؾ};eUDTr\z;_ ; 7\,Ȋ(ns7h݄@P@(@nfKJ\Q@erss)--n#z4' d]'WT*CeXz}3fwk24"ұŗ*?g0/6eh7fWh^'>>鉯/:Ref3ePRRrٟ?""E߻DjS}y0 ؿN9p@@Eӧ r7ӳgO͛޽qcǎ䐘ȝwwSZZ 8srr pT_֤I ]_h̢&5+58edwݤMdnO)iι^|N{@Gqq1_o 筷bq讽]^n@t:^Wst:dAuH?dVG|嶫 0}4(**"#5{XX>}*G?`spBBBt r,fsHOOj^2{,|&j5ݺuÃ'Nr9w/cQ1[;v"IEnݺ ] #GۍW̾o֬9̛78~8k|XWn¢*y8eff a"Xj%*[r.X,lٲcjUXIQQռٖe]w&8*am1:)qmBnهZu7UMB*2#*_f_BAqfЀPcɷ*T]2\%@__M#::{WVVјјOAAE O@@ Q 88:EӧѾ}{bXthZ5kƹsjMċ/(qOj+o7_nSoƍQXXHaa!/2IN6͡aQmߏu֣( f?^ߟ*L*'\7))$,]?zwܹA~J z" І Xd uBK$ߥc^qR31|><===JBI  'o=u&y$[9jZ ,@zzG$7'r[,)))qjxzz^ui$IM6* HHHcǎҤIS\\̹sرc )) FCxx{\$Y%DAQq24@ jQM&QIJ&ݱ E'#hAtu6.PTTVW7,$p_BS?3g8Ǚ08{-CNzz:iiidgg##j%j%##Aۛ0cIKKQ%,,̙w]+6䩩j.ڬt])DMˇG|Iyէ]AYY!^^^U9l(WBҾ}4P$XLFV8@Q 6S\Dن,(J(a~^}pZBCTk+dd2x@Zh`@ҹs'INNv.'Ooj΂$_&-- Y9{6;IJJF`0`45kՓiӦ d-nݚG}{7ߜϡCZm̚5+Wȑhђ?/[* ʈ'00`g;`4ΦW8OpĐeQ}UׅEz? @bPYߗԝ:Hya[I+15$UedQ`oG)+?YKs?υh//z\׃-ZjLtDXXzse +--E[ )((`,XV) ޵k'>>>}N-pXNa/H$;甆 V+WqKd؈ɢسg=ՠPYphT'6"`E IɶRСC{}Fah4l6BBB8Pä^> aNQJjj*)))$''_Y9ȬX^zb矙?s1,[׍X,nKtUWzHDD~ 99ټ+βkJu]L4~ FdȐ;]v8VWT=3<\R~{o.|N:bXߘ?>j%ճ|%@ƍWh{}˿J*Qg^23>KD $C0pK&gQ> Brr2)UHV^tj0͘LX6E!77<7oN[///5kV{!88{EQΦuVhZZ-UUFHH(gݺSݻ3HIIaHHEEdڴ ^ze=F~u34hZ%IcBF 9h49q$v/Dk_8sw>҉aʔgYl<6mEQ'88W_}^o'ԩSe9O?`D0(.@x4K&j1ѫ{'b$36o]̴dvo44Z-wf*saӗ$MнMX,fΞÆu0乗'ikޜ~_~l"[Esκi; 'xl>XhX,VZsnnGxX8˗/]6tI8|0wGnTX5$!$$O?]ɓ'Yd)re* $~jc N>>Baa!)( IIIIjjj-̝;ܑkطo/G?VsNt֕#(BVV͚5sֽ&߷ H~;v:S < aa.RZ2b5 THB-M( EחqQX(`˜8I{}zW?0iD[jM zz&N|~nw^UWd2x"kƌ]OZsd5k?Z\mgW_|YOOL{{|ګWOtx-7K/R-@QQ+Wd֬|Mn$Ib t;zjt?l^xB03g58?7LcU=*PLL $Ivn>%$e}-lOCG {CBJmًA^0#Yiɼ Xz{s ͎c蝃1JUOvooMPu n?|0szS/.+*PoߊVAOϛgNo{dgHQa!,]pW;С=S<SZ46n7xcj4R\1)}P}v |V^ū ??3G6 k֬^{f͚IJJ vX~Bw Sԩ'?CqDKxzNǔ)2es|."Y)2R "F@Q#" Jjy!Ԟ"ZYh0~>\W_2s,=q4"T*3DSӦM2ľ`.5G/?,t`Z}3L&f͚n6rQFG%##ʩN|i 3=kuJ6CRZZO?ToBX[dNr8 n9\ih4`X8s ͚5×G~2f(:UM"R+zN͗ssbO8wE;쐯:;H2@1MhޫzY6g:ݻ)w)299QXXTɫo}({nb@UV ݻĿS+qC&S✿у|K0 ѣ34ީS4vg$7@] ~a m Ŀ&2W܄ 4m/r&JLXM: i ؟tɔBŧ|Xz{~AnæN8V-s:ˊ^CVQ|u69O{ACFaqҒ3Mе-rCgJU{8{WVˆ  \bلd1KaQj:=z,qpD6~\ SDܹ* ^e&NG6mO?UVr8xPɎ;rYbcO9=zԹ(¨Q&))R"ѩSGv: c69tw.ZCwNSZZƍ߰j;`[Q H$! PdlQ*[- j4*d@Ō~3i}q5CQZZ(DEENBve!O#IDFFE۶mh$//s=y,gX񿤤//OzjdϯBWCu/ʻ,3q{9 >hٲ%vDRa̛7Iꋫ++>[Q%""Bf2 Kʐ$E a]HDrF.fItU2R]/rպT\s'{Y{uFSCu>c]f3h޼9yy9sֹVj%77<Ѻu+HOOl6<"o_%$$:?9YVgZScWu^hԜ6Et?> +$+Tjzu/[eӦM+)n(QU's8{,$8ء豓v|IDAT_ 1R8jw6.Zc%bsj DRSS0`X(+-ŘIY2w6 mY*;ѣ?s,&$$0a¤ի0mT7=5V rZDj:O- QQ-y͹4ipxݷo?!*X'o֬)ZijՒ֬j,$K7aDd ԠR} M9{yH)JCU{tZ_\0 dggӹsg '/+}!+W?V t܉.]ЬY3P՜8~׉:u^҅9d2Ѳe VXe9rcFS1*)@]yt_ƍIOpn4N?999Θ#G`ڴYa=j1*Af)رK2,#4 HȂ jn-T"[z$v[gIIILxzҭ[WBC 9Jii)V'c񡰰WyݸP|\7ڶmK6]Nj8}4qqݻ;q#yru-F<`[77 v7^ʎ<n^j]y۬VztFEvlt4:vd>c/+b; s,IDQ6v^^P׏?q@󖭝RdJ8V^k%cJz^{$SoD.˘v$YH(  5Ԉ%HYs ef=el6XRչvJN+$$$S_]tfȐi |;O?[nH(\<`j5v%jMir Wxv Yy䑇h֬y9YP( ,;'*i{)eY/K8+"#LlCԈ:R J'zVm^D_Q%VKqy!NSOBCC9w}uɐT c`gmzRq99ZĞ#෍DyӵKG y.z6/҉QQ{b̛֬fII l6kuTԕwm׻uJxl6"-eHHHn4WVV[vƬNOW7PTT9~옊% AP!DJJTP$A|_c,Qܥ#(aӷqz9N~-Էz|wyEYbԛW-Z8?W>ViED*w؁Ν;ѿ4 ٳ3Lf{ndY?;VhPЍqոϮ0ӳi)֞=%>.==gR:Md쉉统pm}V]F:f]i$9^g%Ņ%%`/ʢESۊ ˪S"+dZ$m_Kvx/o_<|=BVQ@m Ha vE 쥁rrJȠ<9ʄ INN"N<ɁHHHգ+6lE^Փҥ3&rYYYYYy=g&4J8]ǖ-[$,:t$%%PTT(ӶyQI$|JJjɲLzzz |AN"vNa'ts)[Kr}NP6֩T #c}_!̱XX;\ vIB$7\߸FFFcHM|#yϷ}S`o튤*h-?=φ>80Ń]:S'ev15LycٜbBBM4Ae, 4o]gcٜC]+ﺮ?Clْ?8~[8Б#뮡.=jߕ-[AR9yG9Ttk{*̝;T6n܈( (=`L!"IIxCӒDzeׯ7|&3Ky)Hܹsh޼9ӧTmzS_dNAH8X3fh:Mx.+̨U"U=f"h$EBRٱy9q&;IXtRJj*dَE2Y^II t҅?( RKc͕ZPXX{ ޽Yv- F,_(PC :2ixILLtaEQ7MygGr="Çs>exؼyӶ(:ODQDV;z 5^=tYt1O?=T+v^جVF>D5uxzܕ>Μ9CDDDy8xzz1uTbccrtwavmYY]uyXQYP\VZ/mt4]v?j-./4`xpipY?n߲e+ 0 68k?̮].t˗`޼7 <<;t~4?~={һwT*$q-Ի7|36|JO>y&N@aZ~W9^6y'i۶ VÇcXrek׮=zQ k¸qܹ6ÇbJRWyu>.Ws?F&‡']wHթ1RbbJb VmԨ$e.")9ݯ9e?0$ggkK'Ϧ K/dinޕwnlr4>,#jC܄|^;/,l)M"5+;CGխeQ75V$%%hZr':u!%%ᏰhB())ԩS̛_޵Z C6|΀ GW^cG11Ξ=믿k3KE~Oz}=zwa6ٸ;/^̼yo9sseرO>9;wO?=#I11G?AdžsĹ8 vXX=6͛j32CXmjz(`lƧ&‚R&EF8mKV٥2}=Ǝcƌ&--M6qw4h۶mgv@xc{hd ϟ+55ƀ?OgOL)͛Nfbv } aΜ̙SUPSPP 4i;vl(+*sɡCu|ݷ5^jYkm,0Lsoyyy.kV+v^ڲLvؾqv:cVϞnJdطvu8i"LbǑe GmI8q2re{ue}lV}qEȒ[O?ʘ=w;wpIB3Inᄆү_Oj?;I vسg/?b˖dggӪUT꟝2d0~~H4 /4„ 0 L6G}`7s,\^L<'V9(֭+˖- Y3IJJfg00esͯһRGu&{ &8N`Gph:2=Dc;&f#UQ)O6lj"IЩ5~~~tuU?iHv;rgǮ)>^VZU6+wܕO rvEQPnm/XVk(7|:㊯DDiii.kXO4j-Qb)+O)ADΡ&`P~2ܪ *U%`sA}ƒ(DFF2ydVfmoi/@I͞@uL502#v"Hff&VÎ4h~Iy}k +6bĨ:Ӯ_i׭ؙ=Zu=ӽ{WnxsoRZ-laGb(V"-$F#QAUڬV>(-小EA09k=_fs_V> 1hQf | 9j:oߞK֟ {BgCI&[N8Ѡl]N,H~'V8 GdOɹ5i҄V={VaFƎZg'ӫWO=3f$}o-Z,KJٲe+/RNpiӦYGȤIyG[{C!@|w^<==8q~Р9rÇ>>>>Wss~Ua_* LNtBʥF}Y Z^/]DDR, 6T~BRw|,\QQQ<#|ݦJ T"Q6~aXYHCBPv8x4 A*-whi45 ݎ'ZLbR$˙qFHŸmh}J:tofHU*[YHLLu%*T.{z"??㹐x UhTh(t:P32Pw\Pu11Uu()-Ƙ[>_T!F Bzk0(˃?8~njcS(L_Ni'MMi{EK߾ky3|u:k׮[n! ^{iӦ1kl?)ڷoW%zTnnn?XV\Inn,1<)--eݺ5צMkrrr8;u*___BBB믿q^kҤ ٵk7bcOa.{~&"@rrr\mFcl/ܼl=Y$N=B)o~8]-OS%f.ӽz^:A@u _ݽ>/4={.O=51cFSPP?y6$QFVd$ю(H4-؊JSIMS̆NG݊,Z]suraaahтHnjэaQ>"gx{Gp>ĢEEѥh#*v¶m5G%%%.^B#T{+{0\AnזO?ZBDAF@@,'vɊlAU| nC1|tPd…HNcTb/ɽg 矮WPŴqrʸx{ /[9=АЫ~$w1W\Y;M,G\J4Zԙ&77Y0{L:w[o-`ƌ7HKK_ 󻯯/ :uBe͛_G___ VaKfEDDjJT*[nc ǎk.DTTKpPZ^M+P[}oZ1 HkSh`XEAW5~5㏓9Hqo/Fll,&M&0݂WdG,Ŀt~yy(D\ȤPaf7aΩ8W\rrr8pS(IZf͚IdHt-[`[|N&5 cV:! J j8Өy'Pi<<A("" e2YA8d -9= 4Pf DD3jH&M4֭-j*k.K29ZjJJ}WzVnsy7셅UFc gBѐs>-QP# "*" *T 'o Ia*ؑY\DEjB { #jd RSSIII~sjnq؁ 7\RO X (2 QDA "H儽Ȅ^8WwRXHQGUsii)6˦/ EEŗ߿^QdjPP?ʊqQ9­^}uHEaʫL&^~/^\gztXZr[233>}L ٷoqqqNu ӧ3z@ii)sɾ}]zQ|NHm6III$%%gd|ǨTjk*pxq̛Ç׻5>&Sst};1e%_8(nFC}/'71/)/^epkW&VaI0P1&ݶ2{x[t'!=錻A;S*{l-_Hʘ=k%kۑss~5(z=+EQy,W\\k_im4//#tލ[wǐ_rkCrr23gniL4 __Fkgpx1b$Çϗ1xp~dYwzWF||<;;vh$;;^z2z?'+Wvލ'cZm@޽;?L:"?3uWWW>ӟ(wh7pÅn€ ?$,<);vgrh׵!56=pM`61CbhQTUĽ%+VLMM[ZƐڵbcOkfy3mTwVt11G!خvgf?&S˖-/K߾7лw/*DKFF&c}N*҉aʔgYl<6mEQ'88W_}^o'ԩSLt҅G6Z~ɓ6PTT|np8櫫Uu}*%pB'nǒ5F~,Yn7:rmǎmՙG1p~aom+>%HSZ7 h@p~~L4baǎ|GhƌM۶mZ;v+W˺u`?~Qի'fU/`{)(( Hۄ]Ar\wG5\GW=IENDB`sfwbar-1.0~beta13/.github/sfwbar-mpd.png000066400000000000000000000602121450657570000201560ustar00rootroot00000000000000PNG  IHDRI#iCCPICC profile(}=H@ߦjT ␡:Yp*BZu0GhҐ8 .κ: ]Rh=/wB4ctL'b6"^F&efGwq_Q" 3Lxxj68GYIVωGL #8]xf̤爣b6f%S# NBcg\e{Fi ",BUl quR,<prmch]?U" q>.Ш94N3p:0IzŎm⺥){dȦJAZB7倾[{[@z"e;޷k'r6a bKGD pHYs  tIMEtEXtCommentCreated with GIMPW IDATxy]U{3ܩ$5 2lAfdҶ~"-8 8v;4گ~ڂC"PDC !$R\w<KIHB#SsN{>{5[K,y-iJsPWP ym~7UW בhm pBG ̱ch0A`LR !"B Z(48tPC.c볿_*+Zc/Xb)BIa8`=KHCCֶ4Z@" v۶Hǔ"u ̘q$ M6S,) l~Ygb挣 Jb!J&0QtĊd3#90 G (KP@i_aFc HQ 8!#CyQ bRaDRJT?'X!MhR8N**y:uzc#8S \51>T2h6ti߿kB#]rg%B& kFQÑ3D E`mT|ZZRHDaDm<ttN#fO]})e> D FDKfh4k-ڄCVqurS%s $al00O_f3O!?L.b$BJ1ٕ(GU'@*m4tQAec`TZ @kka^%oZJhnC뀺4PT80(btm-Xk(whё(?DB`(T:E1?ARl޼ k%G H!q+`%VXԤSIZ4e uz R<"~t0 ƒHhjjd25(0!B@cL:,J45Q,(pCiԧH]`-vo ?$!0,dڔ.ÔI]nDdK`P J(tBHJ9$" P% UaL*CF8Q.ւ%BZW󸿸\PD$ ZIExwDFQ1eJ;]S ]~U;しUu^Y]EGB*bdٹD*G{ "Un P%!ŖM0%r@c J! DJ5! H)KEvŞ!cҤx3!,L0 dw\xȲg.ihL140ׯeҤIL4=ҙF4:h#P+V>ZR[` `MuTϊf;SVֺz0T璷]mq"sNxd9Ld#ҞC b@P  9<:GONG{Bds<׏}u`0Tby5eY{YRJ(BESS+mijR0QDQ4Nk(/@SBG ȍ() H%)eGI2hcZyRز!ĖW)qцcCHI$:(Vh-<:lݲ/le3+^.Ʉ%wsg7##EZI[|֠q|)1&68"-* JOSj;cMEaR ulָnz-+'"RFVAn$$)A)O01/"LIL&drBi1ѸPY Va(;Rl JDu]W@m"\`"sҕu|FGGɎGcC<`E|GП&2#!+eMP bGQJ$9% 4a(""B 7:A%Sd޲)# kT̒Iex~z|/C) ) "P(OZ) 8A'%V l;c'W8yaX]+2KcAN ZRxdm34P #PX HP G^pD8ߵ""]`O_/ ?CkkR:AQ^Y t:չD0h 3n:&egb4ec(hEMeZ6 %}Dz ̑N5MϮhkE/X}'?c"À,( C.S,2} =/l 4B(2uL1hّ/SI~LwVFrLo>݌eCO1%|%C46`˳ka Rc(!1a#=>44M"DxDk\*~,jx.+@)ka^ ϻ!0&\:KTqlA)cl%40D}uVlu7wNm\)! B1.R85Z NY !,V(G[24 R:.㰧o+}$ s&B)%&O'<(qlz |uQسg)77RRy*ʑDL2:EHb^\il;:.X:b$;^#hmk/G4BJ1 $:28Z"J75NT&y-+G)'V 0ƒ̤(s8*0m$L@pdSv1kz'-M$ ^xA bH_ ;xdKm_ GQ98hSYuUՌ4FW[9d2|OM\.'LڔpS ASvZ(WßD6,iƤS<\>|~!bJq&hQ.[6);s}ߎzq][1{LIf|¨^ՑV8:SkV :-ZUW]CO< ։}FjFFFVb&U=D9EEUME)P(ۋ:He<(hE`?Z)̟>+xM8j8lSkꪏs\vF'aTB9[*bP(ƈ)A(%KQHkKT'O)V)\q%Mqұ/fko \}89mvt3!\6ZJQh1>uuugkȐLIk-Q(Gt(dKb0R,]z/rӄ_*D%q0,8Lc)q;8V,\.3EJ5f/rz{[6ݷ7#ۧpeb۶m|sϥR NnH&\tpM7yd`ƍ|cWUϵ 'xϽfCcA JP*"T`#6 8OҰ3rRjZxټy3-5kfpp]])ۧ0wܪ̛7ɓ'B?]]]R)m07WmV"&̚5z'x8bk :SIb $zʐLQ F*$WE&FGrB"mu]__ -ʕ+Ǚ*,YW|FַՀI&ŋ`Ŋ|>i=\ΝҥKd2tuuc7o˖-cΜ@4{~^7466|?k-k?9яnG?N8gURkΝ[)sgϞ]}v=5'F=Z'? f̘^ˆ xa6xy{aݼM'ڵ7]_3<,_s==@\A1֢x;;{.g}a3-1eR}ݽ$>u&O$SJT9i( sFqHЁhhll೟,6mf\yǘ;w.w}cY,[{ sꩧ3;NOF"`9&$ÐGKk(E!4RkWFBdk;w 6aÆR'J]]3pyWW .=|_6CCC|Ʋe˘;w.gy+C<z477#b2.[0=t:}PB~Xi30s<:s\CHŐB1bhhh0 )DJ9[SkMX$H`)ykF@֭[ϦMY`>]zvu}dKoo/g'6<ֺ:׬Y\ƍ3gW]u%\tхUCc'>~~+0 Yr^`OOoyQ8A.Bikk㤓ģ>616"*!Z@ؐPy( )!Eqp$ߊ q_*uZ˗/}j0 ټySu/~#GZ3N8+3jywW|/YjՓ4o)?3f=y[BpZ[&@qƈD_6$B{ɷmeCA*BS#$ be߾s} 50Qtu8ȹN! B8*eW1.`5ҎEkBc#<ϩ*QQEe-#cK@L65LH4܀%/T]ng$9Fg|NlUC  Ӄ8N\gLsc hbA< 0+ukU0TA]}G|C";6p}76 BKMbg LKe<y^xNE `J">+-B"#J& @kB2^ҥtw=r|W3uo妽-fϞ]wOη;ҥr;~'swL&_\ %8 64Aؾea1ǖ޾L:U 2Hp#Βv, T""bPHe&Kd2oϫ?I)+VpqǍ;d /~Ŭ[ qLDXOݻݽ[h7̧"T."K^زxvc|aOKaT!Ǟ={ذawy'H)IR,]z/gfnfz:w nf>o5K7bRԿ˗_O|*R7[rfk^sott/sU?s WxYɴihii;o~ŋǙʋ/fŊ@̏~O~Z1y}_|_/d WY)L9Ak{ vB&~AaLޘ',aqP7e͓m:r t38G}?3{Bϱ~3qUg…oओ?g?ܹ />G>ᗽ]v_W|t+}O1}G,Xgyf\R& fwܒ.Z{)3f>Ô)ShgŊ!җfxxK.yw)}X89}1?n'φrPI$ ϬO; )r2.'x4[83O.錏yzv<-҉jK/;n;ɓ'og\`Ν⊏RDOO+WU=2MFGG;wvvC˘={63g?EOo38 Og?;ܽsz)Ϗy&Gu/ 6`'s1Z%KT' #lš5kǗ,YL__[neΜ9L>⦛nX+- IDAT1=[n0 ٰa˗?z̗] urmhe4 ð\v7_ C{BMxA>ǎm8eB>nq|+Q`o~G;q>mmmձzS@axg9;8XiXh!_\.[{Ezc<#uvv>jc1,`3|yd9 ,;~?d%BpqK䓫97qmd*U} ###==R2eʔj&8;wVث q(o>G6OB !%RU2H@D"ƍ+ 1ew:/7mU@.2hf:iӦ?<\^5wNb*jcc!^3,fhټy2_.ݻTi-rͺu56nd2Ikk+~XlA׭[GT⢋.$Jq챋x?lD#Ŗc Lex`3<yWM "Aã֚B@6'ևSɻ7xOƟrE6n|n?o)^ nGy>?zEׯ.?OOL2_C޻y)@zjXn}>}zuV|ΎիW9TRF2SL[o߼M6s5<(%֛ 02dՇZ#K kSWSN[P 'ӢdI$Pn"V1a\vvZ~p#DGt(AZ4L}3(V4 &$H{5pjIXi39Ok4Z +bEsQ(hoe#ho?LB> $Qҭ #L$.Y杗Nrʐ3RǨa& _("JPNm#it/q?uׯYNc{K @OOOm 5e:@I41)0.!;ښqBzh?syN?g)橧t; pʕ̘1c\N;U8e,rW, @Ha, F72[z >ZGU5Z*=:.Á}|+8`^Pᬳ\=vYowch⯌Łg9gH(J /Ю;Ge_7ΝEG2tR@ibUR6W&$\E9Nrb!@>Fk\)/lfdq %#9d@;$wZt܆. HL>^{58/}͚F9qOBGrhW1j(LJPwy}8uuu K44F!zw5uR(] Ʀ$wn#8f ?e:;kqB MMMwJ2CSJpq0 .a /zy;bv>5H`%+˿;$Taxx$/pmsRDo ']frdB0-x[{;5LX3VsT )bUd)$;_})S:ضu뷢I/R a)ZHUVaB.A(6n-BGqp</لB~Bvu!_ttt"OA*ـ'k#]Ä Ǝd9% e>xn GSks<2ݘD `(Gh2Aj8,`9~X!ɉqnQ%GJITB0:TC;B|## QxhyUhp+0ܯb٭XY61+Ct KS41>Rq FHEM)8>qSC@GToZ3:6+jg*%C]g%YSnqn~48((e4QdV!TcI7Jی&GtfE TGͪ$0Z[$QI&ҴO֖=}q8Z}b1= #r4jBR);R:_(0b'2L>s8h}}l_y8ӹO~Ws^sͿ3o޼׮]˧>#Xa-ʉ63cy#CYvvo`xx)E,Ezzft45?g}6VN?4nWwU`b,X,K9> 4 CnvnǯwZ__Ou=0j J Vt]S0yLdhhuR{6s 9_84]A{r)F8 PwyRĸ/񱺩80Xd_ Jl\y.JQ(sg7>||{tP>Oo̜9㰞 R;`bp07u'K,9s$uQ+T|%l02hp$a<.O0kヴo\9?ŢE \z]+_W{q?[mr^m4Գ :r!~"M᭤[kgxt7Γ#GSc+X0:Z`ʔ.z}WA/m\r{ڸ|sLH)97s'p?2Oo3=v_z/{.W^ ,m$e̪eE9 ';G<kXXl9W|k#{0'bu}/~O}zUsRMDqSZ*Ho $BZ\g`NtFGGZ R,sj-^g63gV W<Yzny< 27J 3]q4ҙzJQY*Ƶ)~Our$_6u]ϽRO3> g$!=D)Y" WLS}w?H6=sS^ulٲu)k׮#NUVW.[ >N52rHK+EU5EQ2Rj*.Ç|Psg7? ,[찟a^ȡF>{E^} 8[W[Os4D(#]*Jc\0 "f^AqF neYn&돗jm;qA~wEB,;fQRJ^l%(䃸+'mٱ-I a˕4,]3<dŵk׎˱ZvA;XpÏ_Uq*Au΂mpZ[61c"W4J%#!,)㠔OP翿j]nk` ޽oV:ڸŹĦ.@G(r\'I&!JIrm,&5LL[76 U(+ @Ha%D!Z&34 B#W&iEI?R aR)u}ԏpDRqc`ǎa1s 0o 5E^1)-ROQ*DAI66a@.Hȱo:ǑQa c7(A*uzlaP~#Zɝ rq'1i2Q)D]8n%]rA$S.BV T*E>_Ã_8 =`x7.+W0طC[Id0Bݻvd`p0! JؼsK1CqSinoA8mX'z;ijkQt1S'2 %b)̙a"6m~ =N9dyBQ#;J)/C~=YJY NCL832yL=H2fx4]:H)9"N;0!piy]U{unխ)U!B!!*tV@[ilnQ@K!"=HV_9 k_$@$$C%< {ǹ*$"Ƭ'T:sk5"%A"tdE*sG7͎22%HEY5u\T(E6=6z-ϳq .8>mM F]}=0%}) 1%f[atƓܹML6~Z$ ^`? x) _` T6I]wǿqz[RUU_=h-~J0[:hR'  Τ~# R[_G]Tg@$Cz~eB}ѽ_W+MDzoΣoMg7p,ng朅8T lB42 pBp/N&|ÄhlʼnUh] fIEXsNqNk#3Oj6C"?[N|>^ߺ__mTt-[lk.ggOhlAv8 hBt8b )aOn^ c}= y.FF P UD@q^"Y795tFc) V+`$+$SMgJS=(IIDATBZ BN%(Lds#TVruh2Pƶ$0CL~3~ Uh ,rW }|ܹXA@h\7m;(#Íu]8m+ CX9#hA0Zb8h&w8ySٱsC8 ǍW0 j:pc66),,aЪ@oO'۶l-N:yuXz{IkFCQ'QP&۲&KіU R(b٠MQe#$@Hj21!ye/\FGVMcPYYI"UA.G)%t( IVxv>[3RIUVcY.R U =&e44 lGbI6}0$]ꐵR`BRD"A,Ch ZZNķX<6.BvtR4M΅トf|۳s`h }E\$ vAcT*HK>5md.1K4<~6 -HD+Rj\}m{-łbAR (ǍQ])+p\c""F7j>U$jԒLT()|?ĒC"#>D ǮsΙó244"@ێVt/:zZdsfGɧ,ǒKe͞Ia HS#?G#m0TIln޾]:"BHI4Ap,+KW.@6u64ڨIe)ȏ@k:ڤ8 -466* DToj Btɲ(T*ީ˪^EH!Zhv C,+R,ƽe'qsQ cpc. A"$7`T'K}tZZfVԑW2^A2]O1ط khƶ0D$ȭnƲaMXS&M*MuuF[h4ATy#)B]@:Vԧ(.~qU-)hۊVDQW0*%V°$8`YvѠ0[QƀAe0|@H[,];01bXēepy?9t'SHܭ$lG#Uv@1mh|!"UA:]M}jy d,ZB/{DH)Ƥ8etX,FqS gu$ǹ{K"\֑ᨵkHĐVo[nybeRq4X# [;*p,;? ;"v:S  g sÄF c 0$*gwH)3h~ÀT!ʹi'D. 7TW׵YRIK:䐊0Phm. e9 Ybn K1!e4BaBByCؖEeeZ~2 S׀ !ܬEa"<,)8Xx=T87>q|ǒ}$&}KA酎=_yџO%|ޒdխJFA|=|'q;W\uЛ}< GoH( D|K^|={>"*gt]w2 \yG*߆-[Yg-:z{{ٽ{7?SOoEqEaIw a?O~>)G+x2î]x&w˷i 5lڴ-[[[W|/*ԩS:u*-k>ڵkw=tű,|ǝI_ropci֬Y|KwR?7߼^{M?CEEtd2I<g rl L&ٰa}}}se8RJY~}rXzcY/+ p-7~㟼r M(n콣"5ٜU3Gī'/rpDssꚼwhiii vuNzmf455MfaPu-׺bqMFKK sb1nu---:?JQYY'f;GI86nȪU R9k/~ ,XpNrTzBQL3^@@O 3لi22ZRJZZZg͇]Jʕ+3gΤ-]d1Λرc'\1 288֭ۨO$H.緲}{;~믿^v@+_+WDGGZkxc s)}>UW]A"n,K,fŊյ1nN9e.555e{"WYfOO_ȪUclٲeu'|+V|/}>xŗd͚/ޣ# m1c ghiu=\g qbDiʔ 8?ܾZ onZcfG(_d13w m7kÆgyc ۷ogddd;wܹso'LNYquoe< cؽ{7ӦM+z/ g )%޽n}۲e+g7>WA#rUWQ]쳗?;6('!B@R*cKդ3^׮ ~m<'Ƽw1N?_rN`cvf?[y饗YNz{{'l5|#:y8yْTVVK,`1y? D###{vۭ{;y'QJ~z.",YrCFQg2n0@(ds>qW?\6^6lyc*c !;1f͚ Ǧ0}w vz*W}?'੧ֱiӫ 9sflR.Bs=ⓟIEg̘qXOu^聀"૯n|YA{gbѢE<ē{_T~fg93GB4xa~Da'?{9nx ֮]˷ uA֭kcݺ6֬wӟIBl,^eq]o~6# ?Rkooocq z7l@,斯/yjkk<*t?B],"$PBB# B!IJm,k_Æ8*Ief郞̡}^x^~򱊊 .\@c:yrr뭷+?DXSo>ֵ3grꩧ_<^zE_͛_cݺufbLy *!hKKƒ4B+J<663)zxIw$"}t옒pq>ѫK{~ }xwQ>T:?;W c"痢eޘeV?|a``Zg.ӟ x'Z[@&Ut K "`/d֋t)-N1TuJ&@̌N7xH) Ay7߷<}|g) tsOlb ;v~~ט7o!_w#ȑa^9f,EKK g⡇.j\rAy;_yKǤ̞=D8;1=!){bopX ¢&SKuba$3gb,[fy׻~SNeV/>tgA]wiYz2FFFq-[>^酞/a*src#3b1,tg'!x H" {QN< >{xoq]wSUo>VG.>+R[;:vԛt}sydYbv;\{0gq6nĽKooZ+s5555r9֬N=zzz/ /xM ج3ZtD@A*!Ź\%J,{ygVMk_C*f`&?H i߷AJh^t!O>KDڅڰ *kr\D.UUUQIWWW9 X7TM(Y76zgk>ܹsw#eq%jVSS֚oރւuHm pbՉ@N?r\aY)lU`ddAk~:fxѫ"~}xJz[<]l5];;;9~ o#c w!PrM__A~G?+WPSS|#y沚UWWǬYXti[mާt]YGN?|,_m.S2oʔso[5u긁|6|_ֽ{ynswso9hCuu}C}<.ojOxW8b-)3.W.(tȯ3TfM&#z \[O=e;M9x]]JT/ /:SJ(cLGde⑐Xk)e,qQw^Aw@h|!Nٿroh܀f&< (HAZ: oM3|sJCR}H|wUR?r\, pHYs  tIME;$ IDATxg`U R;"(*"`DX("Uv,T)f&KB@}u:{s{=GJKI6k. A‡rT!*M}2$㓏O>>s&$p)).$'o#t(~NGhx$!kr!7:F'ymGQ1T8oWaT߅SwǎAmiƑ HˇkQ#IFM8q5þJLP)"t|uQ '##Wޚk⫕ !\ 9Y櫔 &Jfz*AWUJa'=5?+E*rGDdR*Mjic: I ȲȘF~BJb,SӑdrMG%=5Pv'@`ptxTfcJ$%A!DW,E`5Thƶ#y0 0~I]4m6o.i?΀aCl7PGFʕq9e$ Itb;%L:R:+UXU2ǢsaXڶm#}$dffú~CT":&uFN|u}bSS9:W3Rd2 RT B'+#|ǒMZJ&O>P>\RN#fDp _T$i+x YrT \lɧO!%\d6'QaS4 2k%> 'QF-; GӰ W0oկÉC޼t:4Mn?A"(ˠpI>}"}CEai~n ;u%S M6eC8:Xf>č S P5N=Bj+Cgn/r( MJiwSs&:uv|p>\|ĶmhժמʹmN˖-}u_$r*5kەGjQxUUqY>^>nTLx44Mɧ}i:HЩ8N`@h.\8!.h^GX H>}c0qmWTUw`H:Oz0M"oj8vq::G Ahe3J"I5kx!Njڥ"G+#X sea8|nѿO'NY9fGUhB LjE 36ooZTM8pn23($S$<<{C|u~מʯykB_]7m۽U>Wd0.jz &S eIL%'"4lPyZd8}8mdqp`Ͽ86|ڇk.$'́SYed 4 lF$år$¡lrSLBFծh9r4mҌy_#6ɁhUdff`|G>>\ELhZʗ?݊NgpyǙ`C MU1]QCHW'=-Gp2RQuvĥ{jx)a0$glL%!=[3q^fկrE˓?4? +٧>W'|w}|6TG y?iCycgkDVb,4MCU+ѳC8-{Mx4\]Ɩ$:oJFv/V\M1>d߉Y7 q4ub||LJ2h  *Z )iXlN>ZF kd6bUU5< :"났pUN3_*)iw9\7vSQcw*nO8\V >åA/| k8! W}@$(|:fr Vz\# $t2._H\\|S>P|Ozr#Y(Z4y<&{@LD >߅CICy:LD|B:W9($K IH\qy!NXMLp*}}!l>>\(;N'bSq ܏sA'.,o\?ATA:ty؀ꨑJ_@:]cjaᖇ\k`Qy5=CBVKʡULYe;ݨ@%$I;P?74"1͂˝G4 u2BSRRX,z=~~~^tb"Ubbmtb"R&LJ||(5'Xϥq!| +OF>P*LϿ[CQFCLwADѷE^|~&`߾}~g^j^~@.06H>罗ls ̬lv;,ALF!5ݴt.EO{LkB91 pXVMS g<֯X\CS,MT\;!~e?%_w\v s"2׮8n"-ǑWNYWvMB !@*߻3gٷ?aԯ_K1`@ximz xO{wՠq_;`mk`w8q9hB0͘FdȇC$\ uf$=`y8dRv9H?A30 `( 5S¹y߿CAY!6AO>Wvx 7Pt=|ro [ߊ o NƼ@sZ9T'OUT#4ZW"gbOn~ M/o.͡$QU ** 6n̶BV|3-DQT$I"R3r,?Hӏ-0_RUU=v㸡Yc{c5!;z]]&j)p1tȒ _ O26D0km yc53f'^)k{F4:uVNAI`n @O(Kz"ok=&Z޽$%%d,9FjիǏSfQVMޜ4cG (TU%3+ [)28@}}`"XSRRjP-<{r;&S }ZNH`sF)ie<Rf}Bի*1L`aPrm$JV%dY&B*7գ?c<nߠO58 -75wxVYB2L8!"$E*yzS1 T}:4aboRJs|-[tjT=HHHໟ2||C( $sշխSHVYƈ$dj[B }2㻽5 ,kv&؇nr?_!DUE4!TH U$GЯ߽tKbwdCP7'$yάU(ȴ9z՛[o̘1c@f3},#2mo0z*WOBRSSJF œ (ܔ$P<^vCQHO8V%7IxxXBRҼ6ϮOAvv9/zKq+IY8*,, NWlxINNN^}I05"ٹk7 ]Z$`%4I B&A>aXw s,,]8JI.F>ujrj%1>5w!jI*B&+1U˲r0~sHL)m' )wYٌq8Ĵ>.?J'DQY`?7.  31^OqkucLlM`>l@81c΀a\yvKqp:T+G]rWE9  gcdHfM:+p\+CGQL7ʕ}/Ǡaucs*^BnO~cg|:B@U5V8LܞPM)?@`28z:rj:}7'iz^/Tk.JDBB/2 Yl6[Z SdOk)BVVv^{K˄^b9hywma^zi"f.1LIp\2isb `0PzuV,_Fzʵ|\&;&.Vɓ_>+>>L۶^_3ɓ&l~-#F$#3wҽ{77qzAǩSqY'DEGj*4JB!UT;v |NC +3^`;PDYYY"C4GhLn5&[^[dm(xwId2a6/9eW%g)VCSD>6UQ.NFc e\W [?4RO g n[p~*=Ayd`AV,1c.'I[‡jV=BӰi:*\V$xmn5 KN!aC}AQt:!FUrO'CEhhmnKa4o`Z>nAA&r,bۢۍHbn\;H'?:#z茻Q -J>78<8, >a#aVQM.a*wUEUI̦mH\n~wbWYzn@įvEmJ&di6nX <€ǞO[yd`y3Z^x ԴtjլqY nEEcϢs Qϴow1C^NB}wdZԏR?SȣCgT'IX.2Ca9/9NK) S葼T?@mYhcFnݺ$''3Jx~رQQQ>Uq73S"X7lHIIl6p,ya$H||-t'4$Gr}8p2-Zm ^=]݁`3`$%%n/6ho>EL%IbZ}ƍyx#tޝ*UbXt)?#FO?DnhUxsClذGiݦ i)QhT 6ASCTr vjE Ӷ]B'g,3r({$p1=J}g>v.Y={s-xLfLK=9n6a!9;>q]AvABlڴ3_b۷︨߾Ŏ6mO~ٻ7}?*6NXbPDtbN+?RqCYл̴$& g~f%,xg4 >z#3ӒY>=~zV-&nF bp:dK|Ct2,K.-t6maXl'%Kʵ_7izNmΒ?r)tN#8t`sR0f?B2FbZ&-X4pj6&RO\%8, n_'5 cJ4O]HJ_g UU\)罪1 d\D>Ū*[!x}G^vp8p1~oO/|,g~ *ױr Ƽ:yE<6|4OGjZeiu((K77VeC$т#aQSUtBc EhvLCWᐢ 7bcsQ7ZKUj&5\ P,"Q7LO#(V}{"b%:::OcpyTUeu|=Zmdgg]Ymſcwŭ J{2~p=0r8\voyp\t,o]Y,^|qe$$ߟE2zԨ?-\Șѣ/(\^3=Xbsa90'NAJj6 ^#,UU14j-:⋙ r'N,1l X0>- ޹{9g[eO? ;j >S~=w;I)!EZ|FϲzjP*t.h$I 00!CA'P]vKuٽ{79 -W>W1 炝|At^Sv|-g4#G` <|J?.͆cu8N8!tr16iBll,qGҴIkB>H+jV4-c#Tqى ?zNA1w) kšnYX,!Ib ҙ:,N^ǩ=D1D\\} +/GXsՋwMNIx.'zP73)NEj!ra"2ۅl`0P1Lf?tzIaj~ȲG) |78\ 6Aؾc'_>=;N;V9޳mلA5!4tz&_y 8V$IYf?4UEqef ӵ\>Sw{~/^LP` > #F.ȑ8.\^`{R&;K/~gx\~:A? ֨[X,"C/nL2V ُ#(XBN; 9!JgL&HPl]d"@4>5kب vS(dB"oOAD~W3RҘ>vsd,Bzؽ7}HFVeS˯OFhޘ4kZtFvFDu 'R'^WaoЄr|jx4~(/݂zV?X*VgL֍ZRq]Wxڱӥa!bϳ="ɺsv2ifymAn^ JlFӝ;zaCq7veԨQ14}l>֭ѣB0kl~Wn?|9o.ժU hm۴Kvn6^:-ݻӧObccywQU7|</Խ4jԈO>`_<װ!1!ց ?''>IѣG$ѬY348//q87{4l^U$z}!;N4!x[aa| {v{TUaO? }D:uf<;Yz=6V= IDATZt:Yrcǎ9ڝDڵB0c޽b n啮.$I'%%#G͍oDq+tԱX >>s>-wxx8 \lQU{y˭q]IJJa<أ$''s}qWHHH@;hC_;K"S@*i ~M.]9t%QTDDDн[7Z~AjJ ,JN8xW@:k2!Guf_~1y/)ɓ'yx#ٓ&o~k31L<&MDmׯ/'N`ȣѽ{wm A!ҫWO>Bdde^2<$'дIE~u)?ݻ޽{֭+ ̉'yx`nՋ#qqD3uk`={SpLE7EeaN! O=.;T G(t՗TJdܴd&4/9OzJ"q6n3jVtvlοyuq2=;o?c/}֮_-8w1Ml޼o +=)ҽWr2Ӊ? O?rV7%?Add׷j?XA̮DDD]$gFQk{kF1Vfmp,j'rkm9 @$Ibҥ<ЃDGGӡCoN˖-ځ̜9Up:ڵOSCYl)#!!k I&={^hVJp}֭+ѼyswӦMǛouUw^,*VdmX{8Gp3ͅ&$Ib鲥 0zMСCt 3g~A5INIFs<5tșI&w`2С?O>2Qkߞ7]wY~=7|3fIW|(r :@MU #xk7~؟3#κt\|q}J߾}Yvҽ!f|1uꐑALLƿ8aO?ôiҲeK222Xdԫ_|$С#>}N5kXz5={1xX] #lvnK$fwBb2zvҏNwi"I><:HHFqcܹ3Z⯭`А${/Ӯ];,9+oi԰!9bb2WKMmobޗ_ҷ_?4i}C&M NJȵX`w iܨ\ ]W^r4{kJY S׮,ܳ׮?D(+˓ J ?=U"xzNaйF=dYfML=/ Фqc4n|z YZdRr jbmZFEV=ϛSNѾ];/q׵Kҽ>c111y睅 }/yhC4nԈ[gZjŵ 6Oйs'6xK{0!˲Ơ,!ɱiZ]<Z^O׮]u]4|֡V8u$iiiy]VfϦw;Kw=&qmn3l^bc3pDFEq[owMFz$Iɓԭ[׽ñiִaʕ?&JEܤj3s%DƲi7'\ l1y\nʴixirs 0˖/"8pUDGE1hQBBjyR45J#FO?Ѷm[,6m!, / xA."H=H_<2MsX앏.d!##^JfF:V|Eұc"醆1)ʙkBvu5CqgȐ!hꕩvR#|4 EQ=zϧK.< |WBljVD0 YSߥRm<xQͳXl l^zi"`!^S5*W\a$I|6;~.7,aVtgO% ZG~fEI'W%I; p:8NL&_rsͥ,;sLϾ"$ޘiP>V"Y&3nw.vhA>cu,Y-7j_g֬|_o|y ~.`YӦԮSukױw^S˖^۾$9v5: ~~^nEe>cO;s& _AvR;Awbm;F5g$7K%cs*v0y.^~e e?3 ,Өqv/#99%Kz+bm,hyC tB>}tsgTrN$+LgAFM#ӬK~׋4]j2y]Hq iر^z˯czTZ|nlݚkҤIcv|;7y~waiؠ DƍQ'! ig n0 ?|r4/Xu+LXɄQFAKn|WТ,[|22)F>fWf̙3gv;: Dڵx׹i֬)gP(~wԩSi׮-6%˖^C :Tş.ZY#!dQ}ٖgL 80 "$0gO~ oVĜ_U+ ēG ͟sz7 uَ^'CӉ˜NUڝ\̿Pxo!UkۅzC_e_p+nFnCB1e!>Ƴße˖$%&Tܲ,srZԮ]̯ , D/UUGךX+ #˅E5YAlo)AAs%7Ir 5FA8б.5ne #F>M9f8enjteZМF@7XT,#|ǹڴd{l{;nƓC^S3x~N~Z0BxeH7jP.k˻u UWR|צXnN+/OD4wN:TH^.G4b},s.;vaĄ)|ELLsN0s.TRrqIOOm=2U.ZK'oֿwooߞo/ѣ :FNruƌqնm[V,_V`Bo#bj˿ՙVPFt^1511~,(vOO"Q~_mSc5ysW/ޟ>YfpBt%9.d|oߡ=}7:#^* !0$''3]oBj֬U$Ѓ( $1aDF~wӽ[7j.~=&4wX $6y|=ɾ5d-4؊v+{Ӧ1gڻ|lܴ،k\ĉ?plذS_?o1͸.TU|v9QUoSC2pࠫ˯ׂ[ alt?(k|rF>RȮȝbZ!Lcɒ%]WtMHp0ԩSYn7۶m㥗&J>NEQ0 }!O<<0*Oac8 PyOґlvf L:1:k>3yo~Hǎ#,4Eq?y3m4VZf޽93q:y13gOp=\uj>!35v1ϘRpĕyѫ<iG2n(Tht?0}tVX9 q:yq8l߾H& k׭e۶mE{뭷Xbn7a0T"*E* k~]m/%->CFntɴiQd,ZM\\O>BөIg52/UVy$4nB<&F>}4GCjjDx9xvssXGnݏF%s\#=zߍdn* 6`e't{&RX:=~)/t9݊ےqNgųQnl֯^}JPiUd{kɁ]srZ`ɤeXl߾|eyr*oٳܡ^n;;yUQ1d)ufr ^UD t9`KQW= YCz,%BJ^s93/\ӎ_Š9ovɖ&|'^rտ\@s1B|UUU{ujbZq:=ry7x?7mڬԿ^ZR hժB"8(vmscVFL\\wޒN1LT^ xo 6~9\.wQTYUUξc((*#.& 8Ό8n3*:ˈn,#" k $,YzѝN:$!Pۭ[{{N= ɓu֮]K۶m…+hߟ^gj!j `t-fu[$ XW<27w-bbJ{BA?.]ʘ1_>}z3r \7p ^Ya7{;>=7vq}1qD}1|mڷoϳ> MB|>_L\pw /qwgrp߄ BW C^Ć$߱_%7{ץ^JE~V^7Dmӽ :n,Q7~qwމgݿ/"O>D^^?Bnw% iAAcmcw^?nm @#Gp3i"WWfgU.c)KVg|%^鼗$rgƍR:vӺv'`ܸ;p{5ΝNcǎG&`EXd}߯\{տY*|#'y!#̽j݁V9YhOEÙ \tMߩO * ֽ3;7 2s\/nfqB^> Dwe5 =]DvK:eggG<Я˱:L}#B݁5Z^$222c4د>~6yxRPC=ϿIXY$eӶc&WFw߆"a՞'^y|cf2qh|%^?oVCM qSJ^IBR @X!7XӮ=^`KJJxd4Aк\$U(H`].Eꢩ,ꆧd=>k^_oV!XWl]UMH`Ⓩ~&~ DpoϔW_˯o'>/O>r?Nl$BNf:y!rj$cdI]w6i]jJDe,\܈6 19Lz&HZ)08Dh` )JMx%#Kr"޸;jH D׵1i$&&*I>V)پc;mra NfF,d]=j/2A%-ͨ[n嗣KK/˟;g6 <߰X,?>zD@ <5.hʲLzJbnr9u^zcN LRbӦ&+$qqwҹsg6As IOKux7WsE9s&&D‘K9QlŢl5.*U]uVdoDU=b:V7-M4)Jv#++VWoJJ ͷһwoqfN4w*E-z,1UNłfOWUѣs?7c\MKLZ ={`ϠA z$${јp}sϽl޲ {C}{پy#)dvZK7lC]SuĺO@r ?Qv ^{mLՉ1+m>X,ȒZU?@ BDt]Kf0ttM}֢ގ<#-B?%زqɩoW!DJ[qKfXT$V,:(dd)1 @Q%"jh-b" Ednb8 ]XŢpԑd92XdhDFܱ`DMȜùhEZmˆŨ~el\ɕJIRWYz蒢cU{H.Wbe.d`d{\|͍y7^ UV9Btm ~6lK1.%${{]J 䲬. 6DX3ZS3`]1d{!%PE2?F&m@ XU+1\T;Jsnqs [SE-bēOy5-PC!6]X8&b㯫e:uihZu~b׮C4(4 $xː-E>KMv,wguubXk_K0do-',[+᧗# ~rEbx2Q܀}3\vwXqG==vZ͖й+p0x ag2d0O>ňM#1Ғg7\~儂"?w9 peeLl޲W_x}C㢫lV+oƀ.no0&sC;xÇ m2۵c8SQd^w.=NEVn܃"q |l+i! >y*ғ=o^?fQ}tRse⣙1h 6KT-U¨ a2WW_np8HH(9aDeYvsȜAMs~ jo|{n|~&O~a6`r֮]n穧lq^}glL *W_Uª%zJLgȑ 6 BT$|xkIx?( s'r5'1\~=>;EtӦ1jM3:YgcZ)..BQ"ǷzG?M_ĉ_`y{&"G 6*DʱH+M߶c$#Brn-D\` !Yb1g ]Ga/ NWUY|XZ5 W:w.È: )++cʔ)L~ fa}`!"Yꡟ*W#3¡b"XoD'5D шC3e^_,򋖫#KAO AΈOZ~p߅UAL”7bNt%=WFwp''!!jȲ$EiH{X߶r6E@1J@"P@,Knh^O8Ft7~E$?L?xb "1#LV}uX3KVUWE׺q$,kOsoǑkUUd~ vs6z|+qd!H}%ff&)] VY?$W?]KsVadYb!qcTOIx絋,D:Y9QقnH@ZJ:FH t8|SݻIӺ֛`$MaZ% ko}8#V[3j5wo>kK4=HB݁ S#G CQ9nhXvکMV7U5ܙ\p ́ ̞ջ@Nszw,Ϟoנ*V,G2`Bv4 ɞDÇ]D*F׶MB'8\Y5%*0Z{s4n&arsf;$yrUIMv*'K΃rb4/V2q jIK\mW>[OJH~n3/#=-nyp m[7SZR ~P;eg<"r\`!T6m{!He֍V?b&'I u]m7rsg!? !`=\?ػwrik*gOQo߾eѶ&Ѷm:=e5WN`]qN0jՉetX<7m*^^'///P$医ƜٳꫨM6qoZ6m*?O޽deeӧOLiL1,Mpob!-5m#O8|XxE!-s%';;?ļ{(Q|X\$䦡X\=y 2r) #/&1wQCױH|VWn&~N`O) !Y,43ὰa9D6;_~:tȡeqc@-zuM4 lV w7{vr&GM)r뙬oůEnPt)y˘ ^Y#;TWo\kc b$J"2R/E\DII 00t-44-3?=Y-(%/J(| $9.aa<.>hc 'z|Өc-[PP#3 C j,Xg W+>Yͯ|F UW'9)Wf9ٙ j]p(!XF9WSۀ+Upbmiߣ|Rc6 [gݗ5WRN:ubÆ5s:x tӍq2|v 7` *oEf=OS?-h0'OvܔG$V?8 &eu ]r݀VPX ێ`pI"Gj}(<(Ic7埇]ǀ0M MtԑS;̰Aא^ܯ/__DV~7 2} 2..(rӟi pimPmo~-PVE ^wTUEUUEKC~OrRbHC&ݳH 礞(<(O`+[[mQ6o/ge%$&&Ҿ}{<9hN=INNf'~\ى+VR~ f[yy1U} 5,ÇYF:MkAe&M|O%I~D"-M ==~5va*qba\2'zc\ؤUQEn(0 KSZVJK.s.@BBy'1{,6o pN[ uFۄM y<ŋvDՐ( ј~III8V+>$v;nXU{ |02DUDYx׉6lcO-eU&Lh &L0aDK:^\:G_^_L84V-Q3=|*!I˳VȞ:V_3-5&$$+Bߟu|IKOG69j9aȑXdIFUUtCti^_)%8n,ݥo'x-f'V|Ŋ~_za;FqGs'WmwⓏw>o„z$XM0a„ &LhH$>tBskߜ3 fNΞ<5\<.{UuK:ut% pUar A @eB!|^!+ π+*cN yQqyl2=TU>~k$RM0q8M0a„ &L8&Ç܂lDKw_ɊeiaX^6$I5w%o;jW<`S=F~nΤI8 U aXq<vG(pxB nNE]nć ƾ}99}oPL0aboP*7|cf1"CS٪mp٢yhZŊhu kӋa8s}$L#`UCW2Ot”⓿9o*VCXDqKxh̕qŊ ukS0 ͆iBt8PUC$ ECDi)?Ô?d6&Lh%0M0a„ &ZdY%dXb|I[궯_"$KJad9fQ22sm0 BN_ɋiߎ i5&r,xV֮YOL[ Áx!$I"<>aRQks{ҋ/őv޽? 4 %?}20aD+I0a„ &L H㥨pY͢Nj(H_NZVWQIDAT.aPkͤnQ23=3'B&RZz BFvܞFU}{(ܹOzcF=rl]"&aBPGGu-t>-yhA0l aQ$l۲[v2]:fVC, (Jwyv] *e!ť\)%a BI `L4ݮo&LY82}[L7O*z|6$ayǴoadiFıy:mNS_Ǘ®l\̬6^ut@ G_RR5$I㲑6 ՊDVSȑA pJDiY'Q=*-+')ɌlqIOO7qdejY\1:ҁa >adiE]*ߥػ$a5Rqe92[зE BHl:B*$)0"Φ7kt3zR:u_G"YFhuk';Ǚ0a„ &L ()IENDB`sfwbar-1.0~beta13/.github/sfwbar-preview.png000066400000000000000000001066021450657570000210630ustar00rootroot00000000000000PNG  IHDR%qiCCPICC profile(}=H@ߦjT ␡:Yp*BZu0GhҐ8 .κ: ]Rh=/wB4ctL'b6"^F&efGwq_Q" 3Lxxj68GYIVωGL #8]xf̤爣b6f%S# NBcg\e{Fi ",BUl quR,<prmch]?U" q>.Ш94N3p:0IzŎm⺥){dȦJAZB7倾[{[@z"e;޷k'r6a bKGD pHYs  tIME(?tEXtCommentCreated with GIMPW IDATxۯ%GKfϥΩ XdMgҍ%Y `/C/~4a?~_lȲcf6MYօ/"Gy&Sko}J@݆k|Oxic~t7xk'_,DYӰ٬h _7rm36zܜ#%뜝/!Qz C@'xl`<ڞO7!M "ZfD$[(PJsA"6 @uTUAڶ-f`h'Tc1TV㽀(9;Ξ}u_g!tc@sx{K_)\u$`Tr{?{ogb%6hJ)Z;/ڵS2}uYWoH7nr|rg4ڗ^g,WKP >}?oϞ;?ڍ@Az,ށ%?1JY֛|N3[pxx }׃YVWg,/*?|l1GiMQ1E@)Q|X]1\.C(M~u{9TUwuŵC~}SVz tsy6c,ym9886cc^W!YW"\>nl}>Pr2m:m`s43 Ba~R@Ba Lpz+1CZ7w}xB@ /Xܸ~/ښG~)k /;nX^^p8/jk.|2`ZiꪎVi*[SM3L: U6t]G1kx1V^Bk֪*ܸҽUF[uJW[.sS=fuD 1HNqsMitUUH ymE)nJO{f*ޒ6 ڬ*mT{ƍFoTz~J}֕c>~{_B%REr:Zo ) G8-7]ѯӟhSڮt]Ok5s9wX=}0[NѕBkRRH's=9*9(N/hez6]M$.Rq+ V޻w+BOŪu]lhD(TUqa[ahֆꚪUkX,cW[!$Etԗ'_D CgHy[""hL*xGk_Tu4lł|W_} 36vrrK/&o 3ʯFc׮9@ۮڶ!JS5n=kp6H" H$}Q7 U='k2ecTf:z 6T<dX(l?"eta )PF?L4ś-Rvz7W9}_ٻ%Ev>^,@׷4͜iAb>mbZr7ek s;_洭'M]B! Gי/17k'OuXQ<ޠ͆:te8<:囧7k ΡiIyCl#E׵CʠmFO=㡮UEM3Vk׸֖pbOKQomScBC@䇨J&vza/I QcE4, |Yxbv_kyt<*dVWjk]<&坡ƪmT\^tǬYж=Z`J,kКs@R<}p8M׶9l_$3-Swm:58;;{zlvw~qDb;6AG,f u 6e[hR/iۮ["]6T"{H"m!==aXd'?ɉ5c-Hx>/ C*֧5{![)׋m,j]msd )R_rv2R Ls:lqcLS?`i1B)pP76MD20AHb !nMrC _,I`V߮1GPhuSM7RJuqYk=2aPPr;4UmVģ|Ϝ'ǍSm'i:{8)@q\yM)5%2"?N,j&/ 7T)ֲ\_b])Eg>Dwav?xTO33l%P:J>RД]I=mh7o=7Z&1J1jmΨ?d.l`q lg;N"qml~~zM?}{ nL'~pBu_*NTؿj֟a]5 7mb"V4 mm<]KmC*قvaiSۛqiBQXxCf9Ji* ]Ǭ%m4ڮYTrmY$Ũ%5ORUm]i*6(w`ڵkhm^Q"mhZwmE=\sد_u/o{LWy 3|]Ϩ*u% nZ9(ڠŻ5t{AD}@M%=h$LUaLh&R|W+B@N|K" U $7{y)H\ @Hs]<}|ޤңEDET`+j$YqB)(]lzG\6*>'C f4u~umL:K?}]g >C=*#Q네-:OUW1L݌>w|kSe2S{OY,$zZ %S*516 ۔Db?CZKwe1?yWwXp||Rf=9i/!d@$ 0}cԄ崙4a!Y{.2gY*$fBamŬQ8߳^"w ,E`ȋb/YDhfGߍ9~“s||5t]Wǿ#x?yc:OBc y_rqKܼa/n_Fi͆v)(NGO3qa TpN(ۡ(ґg6toml5dAp>ĉ?ef4m ޳tvP|_.%u]\d!z[|ް8aƇ-_55OouL3'?b>o6P@D~,7orh ]On"3Y$ob"BUX[I:F9!CK82LP)cV$޻TL/7=@][0PU ݚ'?koݗ8usIx\|v1c&?Ktz./'Ϲ CWM'+NX]~`AH7!8$8kO[W"B]1Z&&q<7[[A]Ɖ#3|dx/g !υğ9՘yj1Hj}ƃ^8<8ƚ6M}yb栘ѵ%}}wG|s.鳇%Ϟ=闔 ziWұծ7,+mr;ZeO[Wif zhUUh!G(jiM 3N!B gCJ_d=Bpe f_2?6"exG\\ܾZj*Q21nJD}af w^5Zx{?!=ySj\rE->zҚlT ,+El 1*LG]\o%sl 9iEDtIBlP%૓G%%PĖЌg{pz_z5mjM̎4zmt*kbo^yzPU,nBg,߆EU~uMzDwQVѬMʪ3(Bj6#(');P66Zo:)lsILIM#P(%|P3h[w* ].VQ%X}J!J{o ңMmv=wP 4{nVyn^E %C "g\Wb8 F< eLvd7Lh%i|;OUi8hKAf{lX򻒔h4C[["x#`6Z4Z*Ju^aM2RXe0hsXڎF"q,􎆚&3V6zƑ8٥mWJJ3{PtX3y*ov{纘vcmרtCaKy S*A%[J얽BJ҂s-^~*6L  2f3z.񃶜-5BhxYBo"4vL8??ٳg}񢫴yկ3;\s)OpzK~U[BuumEf>b!'xwjlUw@سd_ʾ窠fjʞ(Hބb"|#GBHrWQϲOtpIR'=M+w_GD\ȚۘE|Ǿ|\;=6d2GǴ}SzNuu]]9JEFG%EzѺR/;k Vt]: n#6t*ssQtIM ӜAj4zptxJu~4;un?dQ#`=$43V˖ 'ڤ ewSgѫ}uteuO>'ѭ0l*`4(m%ʋH5Z8QzJgd>$$)djWUhv|YlPp~m͋^F|>LoK[%9O!x5J&DYBޯEDK *Jr8$zH[Rc#Va0l zu+ml8!a6?{O׷QSb/t)VU5ND죏 549.FэRtb-\cęuuɣ69rE%@-o(2m_su]])e*uh]>8z/ g8HVPabSVjð -)i뺉{Xr1612JTJ9a}zUE=k+ڶ~UUa1ާ2bz(ZWdIf-f*ϲoILd*j *g*}fk[fp UMed̑ JA =}'*Fyu(b@Gp:îsg=2;@gtHw%ƩL\]_̫cquilhcYXe%;<;]۶xdM:ji@5sBR+c0J&Q2oV䴰ȾM||qΌs|UNw*X(wƯ.տ1ztnwdM@׵.=4F 2L{SgT6I6*o҈Le!B0ENhoǁhT_BwY^sHo88S*|6^]WzM˶U>Z'\׶߲Y ]]!x|zuInb/Ąa[H)([UÀ1L-yF:33S IDAT>Ds8`\Z7)?^RN d|r2;8/fتIZto#u@ֱO`]u܈jGh%0^qgbEtsѱID69~f qX =bA| Z+\8'=UmIT*\ !Tɠ~ h6]]Wt ot\VϙD 㒓lԅf æ*+J@Ĕ{F.nI,Q:p! 9GUvczDŲ{Da3ڊajæ۠mVͦgVkfs8ZᠪU 8("7h(֗tZ*4 fspr_,յUe_ T^& %{O2: Is::~It!nfTUa&-9ÔZTatԐRH{>tH0h[zNsq'~gOA+׎op]Ol;BUx]ZTh8\T!Sߠ}nDFIBݷkzW)CdH%_S~#H(DLgsY:~ƤR4еkp]./<}4zslV}xk1ڌ4Ѫ Gۥ teYw-c6;֕wy-py~js٣|t=^zupEgyWxX9@Š~k7x70ٳ3Y_I;d$\e"(+w}F,)5J}DҼyxU7dIL#vNژ$٤! tC`{bϜ-LQX?/ʖg`1x'[S{?r#fas#~umufxZI*xGp.m3U$,,Uz&8` zL7i:X\wSEqKit4e*>. uJ)`B!J,斾UTܺeVwODv6E_^\pU'wY=5ܼ2$cDyW44DR[ |JD[Uܹso$хQ]h#}߳\:5aBTSɵpNEv0sDUM̢=(рn=N[j]Og8b}Klz>f]ײ\.i=Dqtt'zgO?{0_ŒcG߷x tYv4wLuEUop/:7noN${w~$3\m+^1-+-nL{W{Az c꺦@ϚCW1zjujc^rq +P.]p~Q73ע6t麎f3 1 SJE+k'Y, Od>]nhq:B: >Fa i`>v}SU5Jp_BiJe 85u=tم]E'b+juI)n 4|p}ڶ;;QM17Nor]..# 3z(M{T&{oSj>d' 0;= bt%mUlaUcXjlAzO |̑RPh,rTO^;]b1 XSaMl9xhr5.xfZQQ|ŵӗ8<\;qKmͳI%Qt#$4ˊQU+u~E1Ld*T5VY>2b׏CڬNCqGG|b4<CT9dٻQ]GmA|~]ßWFNBi"ⷸ#zYWoQԡ) C )Zݑ=:y2+؁C\v-TGNTNֱ?EeL'CZg$::JWS+a3 >DD=DsNA}J ytlnԨQ+=]W[D85 MZ us+[J^bM=mxc >O}+# \F㜛FrGlA<;vlf lA[02r8 nÇxU9$nfhִ횮|FuȆMd+Z80(~Qjtkʶ!W!Sj5 纸})%ESƘEVΈ' ! ijfyf2y`7쑆.Οu%aSlvN]-0n2t-mNR{mDI!me$$i\P2hhDKBx*ic u=":y/[ >ꓶ `8Jfz5 <Lzfl >,GYzC?e˵=S>W!E8Uh4M*$D̘J"- hc(X*V`m> sLea"(OqҌ~E6_FV m],g\v[%kpB_FY=kel61+[uK<.'5uzy9F΍7c R2HEHGoJ Ɣ` j40#xӳ8' !Psqq#GUcMq!f'i6IǓHFm[hz[$ ٌi"eĸ2:B$iRS ٺH n-H2H=2,mN8Icb@q]MnMBAA#jC}!ضt>v=ƂlV}zxx1cڿW 9ogOz.gкIgo0|O7Oadڲ,qK:.4e^R*jk9t>Ho@Y|w}N+uV}K]5Eiͪl:9w[Q8b*ܮ=Zk:/w꽧v"r0Ldm״&4c$ga&fP>aydXJ%[UtvJo2[UU"O৵TUEe,[\}F+#k?xH5<7oA=[.u6قv\TUE|>ûf$t6=''@ f{=h@wQ5frD ^GqE[J/sQYE);RKJ7|cDݴh {4z;U>XW0ƖJІjoP[LSFt[VMu!cm?E &kZ[\~/ߢ[>9{مXeioq} :ViWsNQ#bN,Uw dm9<8X6 (uh3SC_}J]wxp%fPU /݆sw[9JgMPIb~@ˠv)(yls.I*9Ofohoy?l0E_{@:ʣ&i Iij\8f,>xwmڲ/ZӶ-b+oK~pv~g~%UUh&D*M&הp@'/i)$ c-u3O'mRHh3FA Z%wG~6iڭ>t@EB.֜*іTiۿ?,c1x|To +oL/4))Ŝr[JGmC1C-{Ivp6=eT+aKVT5LxmU uJ@+R ֭WNЪ`r=oegԳwニEմ]L rMtPK`WK%f:?D*g^FP1+WHbkVZ >%BaG8İ<+"ʨ٦"Ls}6_h@e.~7c=nqa c23 +>ˈaFs:HR[ pdz2Zf9"Q%6$y B01]L)%tI[aG ɷpS~ŨV;̷= SrBtXs$L"lSiGO&vH|(o^K:D!FàGFIM口24gm#Rc(O&E)'PW3\ƑN@ɠKkӬ}Np 9\Rv-IJ8g*nSLQ+E iNJ *:CK׷cf9&-ީ,xT mVc9]SVŹU{ABTCOi*zjRGJfP1HZA[L˕PT 3TkWg7T{0C[^u&30<|-ˆlE=[C:)cV%> ߿5R3F >sPGdP>oj/XƩicچ,&wAя1|kQF0s]a2M4}IVU TceB(!Aak{a24ӄP(Zu%q4Q2k:5&I*gKy?% r͇$ sc27\X.HQsKfC=(ShT3; ȬS?>^>j{0[HI!"1HiA𩷮lV|HxpCOaOm} =7_kMXc3~ʅN,?ʡOUMSM(=y)l%kU2[̳CWYE{hnіt&Fsp0Od, IDAT:+Y,aڛޥsQ$D҄ڿC f<ꆺnlge|ipʴ[1Y{QMCq]m?V=70LΖ⣏>+_G(~>#Z[lp|zs|^GK#1,%9 wdsyѿ,R*jM5Mۇ3 3PX|tmdBe!zI3P :?oC=!)(/CfԶj"CJTV>%M#;8"Ny )clMU#c,:Z{ll<-SW:))\JOF?EBegjۍD[6g"?ra1FqmB1> zкgϞ46gl j։4AeS}c];7Ej0XŸ*Q LD.oJp~D6wNފ;wO~:ҷ"88O$ZM$RPYK][s\ﳄRD`/%[nV i7+,0ji wPRco8:YD."ߩ 6RϹ9ϐs=`,JUDgl*2ЃlOlPPLe OZ:bTMvӆiZ`*`9ʊ>ĹLRő{8cxvhSqȭI? 2/ 깡 *lp }ls[h㸭JݗL( ,=q%U>N\6ޖp] j[z'xioF|@]Ux'ts,2 F3zUQ-pK4<a^T{e ET*ް! A$ 7?r{VuWuu7 4Ѐ "Yh42Qg&DcF'qDh&B qT゘'FE Dcf"(4M7tZ{{w?[]M7DZ駥ι.$ ##H[-m _~(zYieeeڂuQ{~ B=`kky]JM) }8Mj 5hNL&,b=:B^Sа.>$WzlO7ӆHREJhLot2(I RUU ʀfow qq*l~BLYT0(%n $eY"ɾl4IZu=R.Co<-8BԘ2.VIZ  Mլ: 4@IeS/5iZ׶}31N%R<E$CnuF Q"Lׁs!R%9%IZfxO60ku[e[\ fj(1bv+%!*PR`)}irb72 7w栘DȐor ,:h2a~X"!fkE!<08,Pq-˃{W&Z*K!Ya8%14K$2=Ħ,t+_c驔y{4gIxlϰrR󆖪EYC7')DU֌@`ea*W$CJHJNR+"SӁ^=xi"J9֞7fzθ>]kdlJ"kyFѣY]n5ylyE)eP^,(ZIlTf8$b@`GUU#N(g,Vy ^! *H@ be'{m합3UR%e)(_Duu$*߃^Ǐ;id:(asRA1 ꤷjWvdή_(&@=Vm'T/oILބ7}9e8u#]L+^KHk֤޷ hZX{ $,xc EC($>ڴ)-ƅ|eY MSaǢģR릚)]{*?%ڡU+U+h* pΥх.19>;0>l)`ģ81>> %TS5k֠6WJ5ťy{zϑf1E"z.*Q6/mtFu*tҽD J F;mfd(%tY>~}\ưf QA g5ZJ#IDNpW Ds4+W#}L};4_C iNg~!>rѸ{Nt?xF['7TCyVd<PJ J85*;RТ7?,.D,!URB d,Z(%ټ8(J=[ި6@$VF h 1r,2$iZqr\\dHhNT8C{8IXq*͇Dd 'CϑF@k\͞i~`[?+C(6G 9}f=4k-Q9-MHWze{V֎(1'on{"V{݆C; Ts+YGEde@x8 @sq*F |Iqm41h#K:wA-zzaN<30 *EYiHv8r!FoGl@ivr OKEu?g @">tJ/\sFN.ʄ]Ђ _Gh Y$ ( '?3jB)^ ũ8`T%*U@iV;RwQ,h #2dh-8Zh D@ĝw܇&n 8cd?.Q[rcRœ.Z%MUښ+IbŘ' R+$?ȠĨ<'$1@!acHX?/F#D4/u]xO,du<OQJyQ/YU%=ǻ`rwQc1#~^'{?s<7nccc\gv<(ԠJQbfn  D2Q  댠kS}ظ8,Hy@OH)wZ!5("*P9ڶXdo Q3XI$ߟ5z lGH}$, }gl*jg)lzE%23vIR 1A:Hk^1נ~aY]G@'%`V/Cvi\dqn>APsk1FC<Җ4~Eػ1TU =\{?㞻b'`nz ; fw#mJ2Fݻb2r]3>|68 Pj)݇bzzh;(ʋHjM F% <kWLq##aCd)%4(p-,q` 4=,R$$U cfZ)J"MΛ s㸙DM(wݟƻw؛ `fi.JGqpedk4 m Fd9H=rd+L'2-all KK=;pN)O; &!fpcHb+,T%>vK/A"!$ U+p`T(  UZE)Z94RJB _k:Y`!$mB!M%SڀW ZB4ZLqhd"'UmOWkn9s0噤I2YI,ſO((,p<YhBd-Avm<us^@#JFGƱT`f6Ǿ9D"6ui9΄ x >N>8N0(vrAKe6D]C\@fQ Fm47*("TPraĕu Pxk7!gJA_PeΉU t _PgRJrxX**aMAgOɂR #zMҾ(tM6:TdbXB,yL$4 kLbA#3,.CVgRJ(HE-T4vNbo7i08 F;?,lx4RB9EYhm͠ n#/rsEX`tlZ :86 BCig&&L4סOC<= hx !cLu2$tУV>3đ*JU1Z>?}I7;im!!`! 4e!x *?fc5mt@WDSC@]ҷq@hPI>ZK rL$ qJ-w46Mh>J7^d&FN~iA@ߴzfV 4,%(M@}ݸ;uXAcM'I눛W5wV>nܔ Wm}%~ +8Bn*?6A:˩R6u?k B`4^mMCs#h}V]RBțWĦvF_wcc4@)C(*7YQQNѨZ?㙧C*^o IBT p(YЮv0{  R*pD7ҁ&T,,NbM=MlydXTខ Քo?ca;C|'!(9&)<C_a(s;ꈛWʦ.SV2fIZ-%$ AphP a$x Yv-͵ݔ8f :Q9>Y'(.1@`dၔ`ݺqCf _%FG;!EΜj g5!Y9eömn [jmpnwU(H?4CI1%o%CpY;ր:=x&h5 oܺ@G@BߓV,4mKOQ0ĬͱV(,Gb5ۍ wRo_CTcB)s2iYhY<@A9]WeI͏%-pΐgKؿotʲDQ D^קm,QXhg-zYfvϧ e 1P(5M#I8ʪƨ ߚN3B剛_0[ '&UA6Ys4zp¬>@Z5,;eJlH^J;T N}uC#h'nQ `яyj%xC`;cPZa1@AAIPѬePYQWzuMQs$( \!%@5$A5 ><EV'-'7!shw:o"j#/ s?Bv (Q=C4 .nnH#}#}!d ᪵fZVj BAk͙蕁Sp,Bif2PShL@/Pѷ4.BVJB`vx_P-lmV,곝:UgH[#`Xj^@dXӎu`Fa +$i& NȡPPg}ϘqtdIe9x0 u褴QJȢ@^dIP,!L)(oHRJ R#A0@F7Jy $-tӜ|yTP5Gdopn:: %C*.1f5_kl S]DRsQJjwBZD]& 'T:-2~pۈ((PRBABP#l^ )P9Ҥ Jj_w#B ҈ 0&ЪBwq? )tjI L@*cQ <2`p)rS$4R07h,A挲V=qd~B CPY hҾ75)!OP Z<f6Ēq4FgtF/..k@⊔??g'B]Uj c*-1n6:ߌqH! ]$ZC e2QGF,xDZR m,(!*DqҧSpF[h95Y.RNc,sY*|qVN5>uo!}7߲}mIykG#:b#muP&2n<tִ_!{vr#4EEHZ h%oNuHa2FXZ4R (+uKߋnf5i=}[UUC׎N!$$uoӈ]i ?jތ/{]w#Q (yǡ94*Qa D{d I9cGKPS;#"ic`O&jGnA E/ˑ;A E3Z>f2p̲I /5xE%౩&g>>[hqD0`Z95>qg`\J~;]xT}"ֿu8_3\jsίΫ`^4 )eq+B)*dEJcv>1W (ԮǬo?j 4RP7=[Q䥑"G)w* pL0FHmR2gU$j3OM$NZZ2Dq<Q/"$45CЊ;C:\\S+DQUU,+bB8J-"(KE@ke(NydVViY'"sP0tQ:5 * PUrQ.%x+uZajjX=:,CAuOiXmyYqBuWU06m<d(H-s;w=(!u6s 6߄tRJԮ ja%qJ#AbdEnB%֯߈Q}?1(}uxlĝ4 RPySTk֮|1JH8E7A)G RQ,,.pJPg\K^̊OB)c6 "A:*DUHI\N4PIBWVT2P>4TU/(Ө]BjiH}$k<\qDy"9! 7oЍ6rӮ`VU a\gZ<PaFQ 2J!B^ERiA+h`BAkN!#PPޔ颡Q&1(.zE(r0o&OMUBBkeNEIi͕IjUۓ 51m+ *$m1`مL ֘iURV/PJ B((DqIP}7-f̈́Fql.΍=J"ڟ35"FXTҪA%+!dLc$ZfhGD_ArdYIJRjpXXe{Q7~a41s&=ƩQ1, P\2fH9P1 .e, 1<),bʩq*쮙g!wUD >৅ŀ"@-.5_NCSo&dkJ>9eyJx.J)4*q1cqn1ppRo+E*r$1mc9,3@(H[MP HfDVeeqѧzi$DUYʠ6:mD \4MH HY!9F-sj8@Cз4?, P8.Nc77"s(q@yQ9ڙ9hZHQDÈ؈dG#-YFb'AJvZUeaLTхyOrPR[i|h|yw ]dc!{V{5 O?1ARBaƘ!0jbkX*R4B#IZk2%M-2J1`Ftt []ׅ~o2xMàAcu]XW&✃x ' 6{o<KK Hǵk'0::Vgn)M^ [p~-"H)QI X!C$\'JX'iHeB"ˡ\fTe"n'Tԑ2pnJĶu\.0e7:$Sk@tQ6ؚ{R1(o %ol#79Z+۷yCj]oU #NRMBe@!f|dp ayz/ NyeUsdYAReQ@!(($I  h,hSTD+M%i!kkk9yF̏My`hP|,Ka~vM'-[`V\,  T]wxcXK$I4 HMڲl !LQ):BTc XNYPm QB)0X\\!(i7 (V)u=XEo2y_AQVl ij3쟞BkǀRnޓG({9(Dn(1-Ӣ(@AQdT웬Z^ UȦ h y,/AY8n r(A}JS̼MpI"MRDq(∓ceMBY:N`M[4!rH,sfI!* SXmg(JSu3@%:Oh_T4-uto7j5.j2L6+BCE:un@wVp7Eqabb'mTmYr@C*TU^h F(06%܌ٜRXKS1rFd% ڠ2v3AQȂ>@r=g!X>E@R̸imE1LaQhͤ3HcO+ u5]{=^0Tƨ 늯ǕlV[fYuU!5UTz>Eڟ 7N8ru_C!75ܡ'j˖c]`~i,JfQ{58DF.*TJBN$,H |k9dB7o*`>7Y?{Y„{sˈ®Iv)̐>a9PU)kasY7dsMSQCBeaDwm Ʊ<̛|)Q0!SoQ*1܃߆B(!fm!O[kVKrN3 hk:ycN[c V V#hۈV Q` _3Zq8رc8gP8!D8T(i+t۵ޖc$0'A+i V&p] *hiI]:h9KAS=5SSVZ1PF_Em-Te ƹ/Jy:TDA/h hSgQ!$cʟ*GHȰ1o0H]e!sPӪGQ@!EuWϭ;ݿΫJQZ(I3Th8nK"ņQQ{!X.=i?DLOe'c͠`qq#zepOגa"'[ (8Ai!aIezD;!u<%-kx_@XF=Ղ0( vߚf<Ԩj,J|Z=):@f]m\Yа>tu2ۡ5RV*u_|I H-a>OG>N aPxB^ͥ~:@) '?#k'(2d55"m1l}vsNǯWg,.`d0?+&gձ:Oj_}cu<5Ӏ!uЏAu3ŕqC7333P|N> ̓`CXSouTNp!?x{KzK?l߭+|9^җ6qM|p 7_-t!DZQ"ic)~NOHc8/ߋ4^EU(ȃZYa˖-[ "#mֵ )iGN8C+#1jk:Aoli j˲{cfff5)E~-VO%|8N8h[7j{ ^9z=qO>c '4M.&ʗ%տ x\xX\l3 vo_O~|'>ZCXr܄r17 U_VTb~)CW81mېeF`ӦM`!Ν;J)֬YM6ɲ w^s|Fl߾_vڅ^PN'&&qƧ$UJ:IS⺯}(޼R%vڅn BFGGiӦ!;_4033O?P ß+7q >ObbbJ)bW\~{%_^z):þ1}gy&ʲUW]~J[(pj!p5D$җ믿a+VWw|;>v"G?>UUa֭I`H %NMo$ߍY?R s6oތ,Kmlڴ SSS^f㎳?6a#;KJ+,{l'._|{W׏~;_MLL|őYk 't.qn׾9㷞(TG g>={{W_O?~zsٸaǎO~B`tt_cW+ӦM\yci|[ge9lwe177?K#vPVs A)JlSH)Śwyex衇yfor SO]< LNNbff_?(ׯ4V}uV/G?hZ8_Ǎ7Ըkя^] /|./W=vڍuAx'x|FƕW~?3x?}5ON577~C42p˰QU?|z]|#vbfM/ ]+/FUzϷlقn.cƇ?|6mڄkbvvfk׮ܜ_itW]Ewׯ|ģ> puPߺu+ '~z\rA zk  غu+>qI'W.:o>úסΗ /|wQsv^!$ aK/B`v!B73rRaBn;vG.#Z۷oy|{.bheՁeB_17YUU᳟  UFG>rYSS.]<Ѓp-!.(ƉVՏsss8c/_n?|?c|_ó#7tWX*,*,3} vbj&CT,3J8/W_{ ~A,,ⶭ3<WU۷c혚څۿ??bbb<-nEٳ'|V<%t> Z۝O3?(_k~zG7b͚5v8PrX\\g~?rXaΗgی7- Yv<dž f͚#+t$3kYK DI@琔7#He$xxn}kjvލ,9OÇ~GuR(^g*{ŋ^B\{7+*y 1=,..bvvsss8spwr{F)avv(`jjIiu0)44汴0HR O?x7x#*+/|(pKWa˖-֛q]wkľ[G\(4 1ťƗͪaQɧtw<6r N;$}x~Cދ/{/sn\z%{'ַ=9,wJcwBwuxᇱgM UUx߅.g .xf{q;w^pyR|ګ?}ָOګ?!MR_~ vC٤wO| o~[7}  GJ3 664ƳGq✧/=cq„' 06:5?Sqgԡ駟.ʲw܁}F^vGpE뾃(puW6w{x^+~~{z GU yKNan(.x/|q]1MSdZ5[o__t|6mk^ַ= PjQ7_s p%%|o[?_S'hjAk*GC #3+BT7]6u;2@{8xj;eW^_Ka׾u+^/!__;,rB}s眉_K=1,O߀ݎ%qmU Nlܯ*K|-ccOf\eضm+z]#XZksw#z5~GC,M f|>aq"a-FK@+]ʰC`4@U*3{ß,uH_ZU=BǮ[/N{/>bjC xM7{^cX8ƒndY}뾹P3iy%.#iLOOcrrpطg'~Yp9fNo^ׯ_V& ,,??4!x,xO~FD~?[_߰WǓTw\طo?wp g˰p]6P XG׾5?vĪ4믿_◆[Y^g.zUX~-'9HhuFIENDB`sfwbar-1.0~beta13/.github/sfwbar-switch.png000066400000000000000000000333451450657570000207060ustar00rootroot00000000000000PNG  IHDRtD 7zTXtRaw profile type exifxWvE1܅Z=~(WW]-Jd0@\s @w翯/XVz)?o_W3Z+~.zF$^ǧJ_*W׃p=}N[Q+3ǯmiݻ噝#S;n&=+|Uj~k'_+C77us<R=SH٥rvj)OJ\k ϼn;pg >_~9нK! &Ǩ4E1"~ԑ=e] s!oܓč+ɡńD| B XC Vq`79BrZ|hu"JAr\\QCfVZnRjQ/jVK^GK-7kZoj[} &! 5sN72町_d"k[ԇ`S*>~E{sw]=)׻bݾ|#mӱ" 1xH%i4A~&tMNo5gkrX?qb;9^$9LuM9;i> KBrN ,zl"9h -:+JGsδV6:6sтdO'rU/, )bK=);BH?MD/}'#qs^1(t_Eq޶GН+QEh#x]TPDQ?#, MEcUZе6!="Q_=nw~ {n;Z0:jb=a@VtijH< dc[:v;G48XgU>μy0_:R z0d1v{ G ujLDy̵TN6D2ό= ˓υɚ.LݺKU-9 czA,}:PmpӞ?ʠ̆tcrVi]h+*~Ei*aZ̳B*e\}!q,Q'P@N$7 |_&St[B W7~x'zڬ@TJSM#Eg*EK"h+i4y`9R@',Ō^JƴJi! w (w`dѭӐ_גGO~ qfd\Ef f`ŹqÓHF(VFJ6c?C4G~>' F)spB:,gAP9P.W`2kSJb8GaK㺹6D.8}LNPՔ鰝ȨzLH@R 4)O{~IHɤ5L# WeoR.0<΍+喫΋GFtNOap(h G&poюNU7[7PbV8X2ZF1*Ƚʒv4>)seg `"SqX .j;X! ( Y~tj*4ސCR CL6hHD9&1zpm૶3U?*X{cR2` L[9f:{&Tu+IiԀSp INI50wÅT1H{Q=t.x,se Q*B<~7OJItjҟE[-p&as *oiYRi NhHTҹDžo!\Yt:iT.Ƨ":WB|skxV'4>>-A~ud"6`5J~k M qa~V |A?|&ʠv({cZ -LAM3w*Q0i6ɄF҉ؓ%5-i%{YYT{v<7doq KB"9:RcKQmj">*E@>9\43/W P56@oʑ0K?rGϩm >mϧu9%iquT@>uLdLq߿ah'AǠw}+惂&d9/5SC,FmKSWguHGI @̿G`X޿Gc,ڥg3uÁQhsўMV eV}(A ǽEҨ Ձ`ڎӁbӟCݩ}gy@G&A D*nUU94=†$rzU 2?=5i[G(3F/)up"ȴz;"- ȿNµ.YQ:üex;4 2Dt\~(Zt?R&~c=hμABT=3rFwwKoiCCPICC profilex}=H@_S" !Cu *QP Vh MGbYWWAqssRtZxp܏wwUc7edRI1_"H$) u_<ܟcP4gnX3y8*rxܠ ?r]qsagFlf8B,{XaV1TiXA(_ȹ\Y5Xu#HaK BAU`!NF '=Q/K!W CwfijM &@iǶ>[@fWm⺫){0ˆH~B7-07!0VuwN?rx pHYs  tIME - IDATxyx\}/{ΙMV/d‹dhzM4ICOi&%7iӄRJ MHR ecc!KM,K43gygVI33:ͼz;y{+.tKEQ@DН8َCUUhUUS.@*׹tAlݺ5ϟIND#DD t""bDD9McQhooG$()ScǦ*ƦjE@'"C'"Oa+PVF IVOa#\DD@'"":1ЉDD@'"":1ЉDD@'"":χND9 \.pADD9ND@'"":1ЉND@'"":1ЉND@'"":1ЉNDD t"QDD9 \QN`1ЉNDD t""b1ЉNDD t""b1ЉNDD t""b(|D3QDDDD t""bDD t""bDD t""bDD@'"-<: .pAD#: {FD"tvvbttHD4y-ԧi~;E[ZZݍ>0w_hFF>PXeua>m@'1A1}m$1-F}[0Љrge,@'"bY$C.DD9ND@'"l1t!E [J[[&~Eo=6׺Ȕݗ3u_,ڒ?x<!ߞ|Nw=`&, !4G"06z Zli# ?2?4i?9WoCԑxuۮȉ?עNp"6IIߓ)+T@-oDޮG9Ձ*T,MIOٔ&aD07˗j***'BWw'ߓ9i'ª04Mm$t\f*}֞KJK_5>q[`?sx_.HW.cÆ: e9{}^ 1wnph49JCDzOӨi] I-@GGǂsRP]\ eGz<' xDXn a0ܮ<]FϹ8ӍeKCU0 (UզU!=z)qi%nG3? YR1ᓔGt`]N؜u ]rpubll}QlD"Di]N NA: ۫c>Ûuy:`AZ&LǎLmE8v2i*4]LEcĊuR“u+WĉL=@Z~׋s]q轞! ̮fBs[GC ȑWѼ}G U47.C1ASS3 R"  +Wʺ8&?rBTtg }Zձ=o8pvTE1 =1mQN~9q\%$R:V E*PTep%liiȴr97qQ,ȏ =Zbڞ SK_"@shxHp###8yO10xcez882[[-;aesRv/| :f22^[E BKKKpUloPhmmG%ڎ&\9z j:ڎ`ٲ(*, xe1^ի_iyXYil:mTucFy)*<vyg~C#d쒑 =e$i6Lv0"o@gmEԠ|I%L`@`{d((]uDQ \⸷lRq1u{Kw4:r4/8axm2u”ؐKѧJ%@r4-\/K2sG:76Bm aC@$@ _ 3Jd+.Gvf1ϦBRB [Hri-C-͉И=Ҽei;;D(dlkj mmmp8)he-zfDHu4DYd~JBʊ_fZqܻ )K|)?`߬*%I'J+ϧ.&!DzeiYl/)WÚ Q^ wq\}CH]^Im=9P])׹ N1ASC./+^z1~yȫ=|C>,[|VZs=G>_N^?w|;¢ EV+tG6D5rñS)ԭ-֮K|Lp}]eT{Sk_!_1V0β~5P8!y-萋el߾.%O< n]bu7īק\zuk8OZTWAZVMb`{rNqK3`K`ǐ~ -_;x}[:4H{KU~Ľ{v' xH0N1- 7o(^DH? neT #A~Y*)zĝ;߿`W=w=_/Lٳwvdt;P-;Ѳۚ’@hiف/?x>Rf||1*[no/:s!H`ҋQB]u˞bBt'SSK9bHjEbPYY%K*eKEjj);{S;mvDڡ@=wOnX6t}hGF~Lj-!Doi$,)tjN:>7-ǐC.3}~۹ϝ%v'qC -w~CxB >֐8k{kx|Q֏ k @hm}1%^?|>$?UU8tic㨦:iIXP֭>1z, l6Lצk):1RvRb]qIJ e±AW )X_rz CU477A q`ю^>S܊xo3p-nlLTU?;ũ ]B9XRYK=¬*%e`ǹEal lzGr. ?8*lk<rj(aWVw BF;Hn swJn;w@Ux!UyҨ"ӌKչeX0L aWcoaH{2\(UT;6z}nDÃm/VSn/RtJsci*^|y$r qӚ5vxx˔0,;MӴm0L<7Nvt rej^OO`ð=}Ѕ}y 珡g]-*vw b]~7/ ŷ^G*csBOr=Cc׮/a/C.~ k6@&yAʊs$Iꚤ??o/8l0NѾ 'vP* yu#y=sMXĉWGq|VPIϫ_hQX4ݳݹfeԝ;zixg@c7\7J[{ߗj&޷z?(ڊ#q`Ѹyn۞"GG>M[Bx=z|CكxBUͩBi8T@v.t}8,Lã;1?D?S:vv߲OcܳrC@*+w:[qv( fEruq%n3徧A3}`K/ݡxHN؁EW_9oM ʢGyDn)XR}ph*4͂pf@T 3Z9Yߒ1FC xCuДħ5@Q^e 7MoB)ө31Y"++a4.M驍)Q3O+@DR2 ]J@Slm{ŢÇ[oOr{z0ʖ/<"O s@LRe_ ..Ă`ȶ }^+pC̺WO0. R2Ma@F9!WRB4M_ )!\.b;anAUT@SOyk \Tgza0WQTT G6^W3t?z5;5ą&,<^kW>}yf q=.{w @q: ܹ6ުM AL Gb,*_A J O8Î;r9)n _AC1J5c@,\hɨmXUUjhm} keDTR<`Ce;!c/*џ EԊ͊QUUZWk??St:Qoh+Q^V|׋;w[!:〴漃}9)KBWUsO|D ;87ɓ=WC̓'S{A+qe-; eL[:L< o4ܧ|VA7c˂Z$oKő;ǂܲ,e0M?jϵI_!%j?pBZTWCλM sUp8 N~mCRPQ[k0xÁ˩~[tM_"le 'bmqʕ Xt]GDסɳBەr>9 '^š^/N@!TծʣPWDOo1юHW`fpnjcD /ВO###0E6]UU{n(Բ,+-i #˗L'VխFShp.,_GY7FFRS$UUMJq_|G@z0 By9caDKDսǏAtwwcذaCZ-D8FiI~ NbY);3{a@!uݮn{^ ]:Sz N1˿޲ Mu}^,-zutXTuH)ɫɊy9Gf |zv7p>Ol^+12 ^oȇPhp=b[ք7?vlYc3x4\N7.$}tuP06H( ip辊a8].\.80O8%嶹 \@ϱP,{aX%[x3a~=m7C+B)}t+ Buȁ 6.+PŞݡ0':5|8.<|Gb]"7oB(mچqsޑ sbgokl1oyc#D LV.pADD7.6Qn`1ЉND,sibm@'j!ٷ26_g#DA[0Љ(cJ\&1Ա̫5Sr:e &. Lkq* iz~~>##RP~Ӻʹ\֬Y}ǖ&"gǦM/5́}sbk͓!|<0ֆjl޲?剈?~sܵ {܃;sZs`ӦM0 +׋F<:|'>fv_B4r-2~8muĹyDD9Љ(g#,QIDATėMUcU()|-,3)DDI8BDD NDD t""bNDD t""bNDD t""bDDC'.r "@'"bDD@'"bDD@'"bDD@'"":QnЉ(gp ".('vڄIENDB`sfwbar-1.0~beta13/.github/sfwbar-tray.png000066400000000000000000000572571450657570000203740ustar00rootroot00000000000000PNG  IHDR#8iCCPICC profile(}=H@ߦjT ␡:Yp*BZu0GhҐ8 .κ: ]Rh=/wB4ctL'b6"^F&efGwq_Q" 3Lxxj68GYIVωGL #8]xf̤爣b6f%S# NBcg\e{Fi ",BUl quR,<prmch]?U" q>.Ш94N3p:0IzŎm⺥){dȦJAZB7倾[{[@z"e;޷k'r6a bKGD pHYs  tIME$P tEXtCommentCreated with GIMPW IDATxw$U?'TUwOܝKf% dPE" AT?E/P"WHE%.A`ٜfvR ӽ3;3캫yz=y^g]cp#עО# #*C\Di*9U9¦[k- &'HEp+B2Xhm%Efwv#E<aJQ(Q$JlNdZ&1TH*=^g-p8>(l)!B| $f鴵S*I?K)cOQJS*f}/Bve/.J%GLВaOo cٯs&CXv "X im "(/C K$X!5HRqL& IOG&eԝ)&_R DQT>H!\Pk!qd9(T@Q Ǽ\u*]W`ў"I9ulo=P!O vEȐ˵0kRfue)h(I,$ID=#&" q,dPJ RC|A!D킃 H)V `"58v^Ƙ90vaթir K{D(1,8 Y1I[G,rH\Fmw!%R ӻR)+ws HDՈj ou^>t|73Ol*$'qއY"Nbq,28g C)]UsX@Hΐzm8IumM]CAy~ἓn }꼷C%$"Lb$"#aL)  C ~y5oGXaWՀUM}ԘLTi*}̷@"ic )(Hcb16&"t %Ӊ3oP˨LԻk竣MuBT&n 0yڪ4 !,phVXk(G!J8`hc PB !<_ER>9 B#Ou47pgɽjMmjl;{{zˎB!&Z̴iW@1l'aؕ&5 c-8%p4D] 4RS[gL&J QT", ahLVgfEtdsZrQ. @>i2S˵b(8dqmAaU_kUkKNȶvq1n<%#@}kpBsScPBR!̯g L2IS<204"hଇP!qd8x(Oy:%QTX|q⤇ A g!lLy$N=$dE,]w؉̣# JIX!I{r] ]']ews)Ry S+Y2(*yZK?IRKXa)cRb%y >ɪhi *[ڈ$IGK?eR4+-'ihh /^^W9Bcq&F: 6BKiu Rbx8c{l'cL%7Cdb2Y\86HHU:AߛS'6?PFvwUmZ*^Ŭ3k77zu? (8g|EDFXqࡇρZ$K RI W-EkI(Êꐗ$ ZA5p!馛oaaǖnlZFH)~l6U\U\51`Q*Yv-}NK>^/-:c6aɒ%hAE@xeKa%aV\O͜9sjj+_~ם|[~=7}w{_>=s_)vqGo>ϕ6ŗ^kW\.o/𝫯/lnF}$JSl6KG$]ڵ=(!UCKs ?OXv8P2Zø=3CxZD V3)u=3v7k6~k㎣oo6#׿]_VN8X\Bn d5s&x O/xN;TZZZ8䠃xy mǟX,q9f99 s?G>xlN.<<~{mcx'眳"}}781G?9H^|%,] K hb9 /EΎ{/˘=u} E:;RnqߝwN;nRnc )!(K^ֵT ,| l>h<:gO!k%sd k{WcM0.m$A@E$iM1 *dZ *EK['|x.]ʡmw}?G\&mxόj{'ի4\nx0oޮ<ē,^8Vftvt4l9?{x9c6{w~=k2e CC%KQ|zccqԑGrioe}㏣c_^Ľt}' JDQv%+Ѓ cVad9H$JxL.swo5Oɋ%Ǿ )%_,]lx>YVsCJAkk+sרTֹfcqƣ{]gvv8}\r{np7^=3fl ?wmߋhk~6-19'EZyk2ɑX|Qdio[0$.AX|!fm_|ڙ:c@H5=+Q:] ƍyԞQJqWpWOpғ(97;=O=ɓ&qx<섏ktuO<;sQSX, /x{x8xɧxx8 oٳf100аݝwC˸Hv[3=s5 kɠL'Ncq($I!#184Qd:| k@JMgH(ņMY2k[!`=iXK EUKEN~=DJ c8桿anװۼy|+_c`p|>ϥO^{duQ(Ï`ԩ>o_ ::Ggӎ;+=8Id}ˋaEaY[pZ;drJl 3 D (#2A IbP( q.JGi( 2]S" E4@؏9vF۪\o}+GuF;l=sf7ZN=$yιr\ҥK丟?m|/C<[~g ,|7DxI E.\+|9w??M|xYXߚ_u;hy{J |٥\cO 8䠃[O=~]~z8yocB BGajPXyWUWNt&_B)h!Њ֊.Pb-2ֲzWbݓ)SwP*FC!lB8gWb :y\o6W^7s\"x <#5ipΒQJf1.= LԭJ,x&NBKQDQUTR+K Ia!x@s* rI'^M\__moJ !^X,难#NZ"IR ړeZ QTE  U.¥'.@r$q@:ZjTJtguҟfzC|K_:?'tvvroyoF(-քʱ3D@ȸyP.BJĐ (p4 S[ K1)Qb4Cu$5@?h^ ƃE iMbpi~.OhH)MFH|?jWWe)]D-U zK].IbRyfnYaH ֆr! !J:MRi?뮻?}&Zy^@&eժU%Og}i6jt'QGkDIe1&eI,G}K !JŐ$v8 n4l$1!R s ɡՄPcL ̓Fx_vi̙=() Q`6s<ȿ n /ѶĠj BG&ȑr:T-L)EE R.đK8FR()C(Ib[VyW՚8R㦅 nFN9|k\JpNW w7Rsk?hvsvxᖔ+֥F{JBfB"e TH|Z:h@jv3vI:w<*B)I9J+V"mRJ;`p5p'*2A85)Z5@AKe9(ؚ߻z-8xRH1#:G6$&}!]JCIb'kKG RYN8jR R vr٪#p]nے%K;w.E)Rгv9ws/wy'+Vx*S.:oO?dglpʕ|3/<=|/YZ{O~z3o;L`|(_)>)\ 8p}n[]qv|#e񒥼qqo挳fʕ77qyp%ýu7kZ<еQ"z( k]EPnUiAcxd2kᏏz?).YBPbJe>cC}L~ ~e˹䳟؝j(|/Ϗ4  B8y]1>}3#?7{mN0Of6H)P\ի"_1ir;-Ў06 2(-C4(`!A`N%M"]nx$J85) cQ?A.c֌)JK% $q4(SxG>x---tM̹gbq?iCcǂND')/]~?mieme[PRcK[ߘ p)VGFDQʭ^bvu>zXݳP`!"61m3c~яs/``OQ=d9KDNzt2:::{gZ+4ib󣱠ciCcǂNDSj~@OXlN-(P$AJ &DG1^@{8ѥ*CX9;eƬ(6ժei4Rj` $ apYP(%$#Z9rcN|Sa8WϯN9 bŅk={$h"^F7d)\wD^,0=7+ܐ->T[ydm22}(%bJHoHhoAOGi u !5D`kstm'e:g 2$DQv+c&f ϳ>,k34'1v)sgc_.|xm̦3f̏$n0l*mp,XXM y 6"N9hp䲭!KogR kW&PsVJ%^P&VKq86Ttmhw3azngfD%tf!ޕ;bqFǁ6o̝7V<\.Kkk+]'`# -YzjCcǂnJmnV&룵&,<3L2c&wME r=3iLzRTF='Mf9x^ڵkè®yd2ZvC!n9mbT*4kz &C!S@PҫZᛧJ)v1\(Lp. HAT` vú Is_'?;Q,,e m!VX6⤄jU/ٶV(CLY ð[ƔJ(B{.4$ՏZRJ!tԿ!^.MR"KFe{tH&ttvLfm42#k5<fTS09.2MiNMJkiWmx"Kzv&k ^cp Rl E&K#47.J< +dy|6vjjTkkƘJ:0 dmKbt-QS,Z7*(%(q|>@4Oh1sل]BZYgg'K,AK))U%'em~S Q7|8SfVf.!rˆ9pia*hB:F~պVKA8E'%HvCe[wWGaPbE.L>ΩvS*R=uoBe+3U)M[7>dh@~@j]eUME0If]cjm!SV<I@$D9szACg}jd-hicxz LgebTz6 RgѶR 0QZ M$x""f8 FV1]]ѯ0gζtwO!&Ve %0hjI ~%(VEq\ Z M Ie%^5DʪFP lb)$IRȍQr-Ęm;ȼy8 kx6~S,`ͭ97 ,&MN3klG`-bH&B&I:%Jr,mm qړt"#G> Ҿ?744!o8t#;Ԧ7XKFh-*3AL'-E/ N WuP-avΡ3bĔY:Oiđ%"?;mSi1 d9N;4&O\{@ͩG?A:#̙ͶlOp@KZG@T*1(#/˶rBr~o׽ɴvSxJm -Ɔ ==hk@TZ뚵DݢXZl/z訯eN=T}Y|8Gl7_n=gxꏿ hG*Hc6Vf6AIy51De7ZU+K$dp,]ζJ/mj\ u89ӟF/I xGw}KG6sfx *~><?p>-_>n(o}a 8qcHHhƱxq '0ƐϧzH@faxg??3"vW8~ζΣ57guU-v9R:húݶ#<HW^C7;p<_U7|3_z)Wd3Y.d: MtkY1-oۉS)cGyy=,oX3Z{!H\!$9䬙5)Sc! Æju UC{=ZlT^;\&8+}$'H:={6~;<@CTZk~ۏ7svĈG4LYp<_Uo}˩vtvvrO>@ᩎ"'B' =Ґȱ8^3?hy(h8)ݳ(.380f4&s֝u:Pt;e*5(HdBw '>?A/pmxbE78#ֻ;qƨsL&CTA_* WnW{ƌ鏿zMn o"ƃ:n`- kr!+WayXj5zV1kv %e[# Q2/5<ʥ2RSo@ 8HV:)'ZkoP>_~9~;?ի93袋ܶ:gElԧ Gejl o^woo~޸J`-vސIujXdMr$LPEya9l6<;m6H4c5,\>ϛ7;ossײ`z9X) 1e[KtT.О  u%3qRJu\N6̲H+ Lc 9M:)RP$Th`X}M;i#8NոJ}tJv덴^wA5Mx>`A74W6p1v(U%I{娖( ؆rum/ #stcPWW \8ΆRKD8'kS'ئ7"80Կ7QKfJʃd>lc Y0NUs"r@D!6쮍%#N}Ɓu9ZꚎR1iu"]:|`E(M.zBi-R֩Za8lK q(ؠ$xJIs͵ AZN}cM'ԀkDM7N:>N:eQ[(lT*Q.GHpꙑ1~;&0UVL}4И5= ]a]58'䶽Zm9icqϮjcwy>~iwomQrdIb 8xZ 9GхBRmB5m2p`^cE֍UT(mFMeYOF6xsRJ.g9=gswnvڔy!]c2ʥB!"e9V gAȑ EF{~HZz0mbW#m//Z-ٳfmYz ϱk:@LЁV$F Y(Q FKt} lS=DjMDq6{&˗]ʕ8ē`C:hB,Jqo=T^aw~f1'|\H.E[['8T.Bj>al=.IJ?V>84 sF x+. a7/ܶ"ǒںrvڙ`ЖVIP%}p)8&h]i!Ғ6 K[ؘ%HUW@,I I~kd셞&-wޟH͵0idРKtV Z;h.\JYIb%4JHd(Gh+ kmSUR 5mMmI9fͩ&OZm!83YıAijL<&%|,I-`E6:80)8MmIEQ>f#x,5Ph5*V@XPw c! fR[Z4( u0C!qYjjT:$d2Nҥ)aVQ7L.VVhDUQ,$acC2֦E*-E HDͳV)!F&;kd2iQ\bp웆9O=#1K[`.%s$E[kR9",t'(-0ImK! &()29DmZCk[+3cƬ4 0gHԷR=uLHKOjʥ&6baΏdkhR$4JUa8q ; 8McdZZTH"e6Pva9$% QZ4 '(a4|h8B-Yzz $Yjj2})t* Ey(Ҁi@}kWh&M;e]w[䡇l;ڳ } 044DOfaU$jy)iL=~rw]md+Hit{yͳ&q|_i]g,C) LҝOǴR*My;??ţ{T+1 wd)ײ|ً /c_{ ~e"Sdb'|ޞ5d,.N8$/^bRTk}$I4Ғ!q QY, Le*&-î }Dex$3goǞ{rAbJ>nD%-!ےwKdԓOG_dJҎ^|%xGbJW|vwOw\Wk_S=rRG>=ɓ9i*S.e[dr'pJbϧp8i0qHS*P.ўdJd 8kr7^g($E._S.@g3q:I)}l6K.#͒dh)x8)z$y*Yڏ=gu6qs>O?ş䥗3Ɋ+'CV}UEKSZӳ %%Ι&c>}:x$gY; +H6#1{$iP>`>OT$ X&))L:RBfVaCj1ck޶,_>uYwF)\㩲RԩfگLBFd0Yg7}_ߟߎC>k6]'s׾^Ydis~ӛygܶ*o]_Sr CiiA#J"@$DQRssEUrhkvkNjxDAu I%tJdcw ńO4xu0mj/jJTKUM#L}z;RIٴU3fx|%|>J(,QUQÈJDڝ@IaH +IR*F9o7@f&0z4-bE>{rUb|7_Of ㏏<ҋ\<1pr=.g09;v ͠$Inrr 5I~ b/ ]< =$XUa3eLCĶ$$QrŸvU1DZxdɍaYS%۰ XYb^w xkwEˍn$lI6m @"i" * ں$[vyӦUP x4Aii9Uxr)(GPs0 ApRcQdE&)Ȳە,1 ےp Pg!7wnhX,Uu،Cy{i9?ǝ.Ey⑇<93Y|/8;;`CŢr  bJX i8%'_jjl oeҴ0\lLgv&zWINAFJMˑ\Xv杍S\TE#ah|{ohEAEaH̙&˻C>k:{>{?1D!g݈:` ғLDэAGK~nn[ kzd^,+i詬PI 干EkRЈD wz} Ȋfk8N[G)&rA8&#v2Igp~c(8DCINxTjCG[3 ݏ UQcqN4U'R'B3N9 ! jj(#BaaQ4޲nTRyy%O>@+ |>m@}l}yilڼ\* !Ji2(b6m`퍛!٦5ԅ$I[v""NILDӰ;]"uU3.1RI)Jqt=8U D[ٛp4Psx(nǭy748hܸ},*#G"ⒺJf۶(Xم*m9ԢnuY* 1q¨4!:0Iem$,L%oW_ŭ4q:Mͭ555ضMaa!9<$(dE2F VMCO[d( HB)İll{&+l)$m  /%"&.@,dI"/k\Ɖs =!:`>N! @ _ԙL\y>)QR\T2Ee#^U<ť!nlNn6eL$mmdI"?,KEtL[@A+.Cv~\zjCM<:rba&|LmH(!f:lb1)6c?ih477 #777m 46[uR:i9+^%Qe4bH`0@<OUΩeiNF(o˴x$IƲ,tM{Vv]xp\6@Yǣ$(*aY iI H%{]x:I%`# RF¾$IV0T! NWg,[d63Ne˶!;=?:xmFL=!KxEǖe,3./f ڦѶ7Vs\/r,DX-q*o2g% PTT_|Sx7;[xk,_杴[(-)9gsԜٔ=:лޥOWʠ1MJM;ϯ)[K6<y=OWoC۶1MUUﳽ ۶D2J*O;|?fB0&ߧ|[~z1}]gzT|ԑxޅTd;@]7VSõx "sI7t6cz0r*qY{AƤB?{s0 RGmoqq1t[|S4%i#u*LDͥ[@nn'mol‹~™_?5+ ,7Z o!OrٌŎp_,p8̿^+ẅWq3'[IODe R Aj'۱y  C7M$Y`}gbD 󩪪ML `ͷGc2 ̷S5lljbV \*eTWUr=ޞDlՄ{? 1yJKJ_{eQ\\s.|^fN;D~n?g+8UjÑ 鏂7~B"ٵ5㦤" wiMˢ-F %,퍴2ndŬ]>6^/nD2IRӨ߶#Fۦ ܊eYL2_~+jDo-щ#3N[Pn\1|7SU -(i+vPJlb,grD4>cO;((e6N|}kr,+&Ը;$??o>z.%t/xq '}x~#a\wGMmWHjPEl av:2+plySV;%4gul߰ZW[ۦn쮮)ۤ++Dy 8\."_Z,'8픓˯+'0gWoΖfl[O~CxM;h$IbάXˬX2SmG-G'!Z4]7 E4ܪ%OpuNTm[p Ywnx=(<ħz7jKVڥSzizZX*))*&??/mTkF{{`;[0-P8̪ի9tڴLwu|?ݴyqG5-<}.} //=Oɷ)UlqWZWȌC>sơr{wL8 2M;2նI$ihN("*!@"ij;Ei 0xW,fxЛ/Օ';vY]n=JiHk}G Bmt93I彞_prW/j[wR]*UlFII1cFl E>}ղiZ[TWVԬYƸT?<&+rk}Jי/Nu u(}!;v\L?x*2qAD"asv/9ga!7lO>~q$5¸qc8hX̙cx#AJaNK""$#6i!X&]m,䣵 R+ O)p¼wx$Nm뭏r>k|^!HÏ_ #rq'sryzU%էG϶lZKt/YV[R\LF`3u|ǼڿuϗyWAQ;+ȔI`]6۶l0,IeDIrL'J:|EjI&要KQqyrI5x@S2V]}wKYz::c ўv[UQIFm)'[ϽҀBu+k786quee/jm3;=2WL5t\6(%HI$⨊ pvٴq&LލFwM\-|Fy'xqYa1eVCW'IJwV0Kwy9$%Ŷ`dCt BN; =φo/t~;c:j"u;n(y>XISS3;v䵾&qݲWLS&whI*_aa|={P%/̩'@yY 6o2m%0$qĬY/gA~4t]'if&NIH%YyeIDw"N {+- sk':^LZԚYwP9-'-f0/vNT&OW@}>N9$Id~vعdJ\oL]Yi)_;X}e\eΪ{\vEB~ N:XJKJ^<IM#I1 #'Of5D\j|2cZ^dM$a57#O_u ӑڂMmT,= 9YOg!FaQl5ÏiXWOSA]-<N9K/ \i +g,O72[ioI d7q-Y*Z[t8a<%E\E p޹s {.SI:6Ǝ"ǜ}D5-d" TT024:bBU ϝwbn؀ =G[{4JJKg&uѤ4qBlx?88+:c:cFm}{EmTӟfP?K\ $_QvϹtլܘ]8AKyUJaѾEW/`њ\xBjwyƌ>9iڛ㵈5/wV;T4]òm, sgTWؖS98  sZc\+N`!pueuiD;ٓm\}"`VWWsܱsi_;dYT~Ue>s\r<4vL5sơ\su`Ewٜtq}a&K^\C?I0BE.w!Ϛu ; S^Va٩ OoHX]7NMnkc\O5eYBTQ,N't f;39gSuͷzeVUEcsv}oܴ.|#23˽ΆJyE97 w|1?lGIذq#yp؉x<\3 f:,3,o٫xg0,ǓySn6z_w C|eSUcƎ%??ӕ;s HNJF?[p5?ؽ4O[o-x{uM77 1a}7SOtjKEʗwyNCO)h٫=k\WS8lX7QeeXY{pHr} ;-@ѭُc&Z2oZe!e|+IENDB`sfwbar-1.0~beta13/.github/sfwbar.png000066400000000000000000000433011450657570000174000ustar00rootroot00000000000000PNG  IHDR0iCCPICC profile(}=H@ߦjT ␡:Yp*BZu0GhҐ8 .κ: ]Rh=/wB4ctL'b6"^F&efGwq_Q" 3Lxxj68GYIVωGL #8]xf̤爣b6f%S# NBcg\e{Fi ",BUl quR,<prmch]?U" q>.Ш94N3p:0IzŎm⺥){dȦJAZB7倾[{[@z"e;޷k'r6a bKGD pHYs  tIME0c"tEXtCommentCreated with GIMPW IDATxw|߻{%=\z AޛDA(*PD@EA 4Q"'+*t:^KR. E+/nv晙}>3`YUQq8(-DIDѢ( 6A< %$!t5FJR"]y:ƒZbcy:<(UUQU^OnoiGWjEEτ&4&+4!;ddVCHy+=<bߓBPP ݺv%442y/NyC_\$ idơCVbb{:? H(*[*zIO_SbZy:_,E/{e5h%?O:^|^^Z˷p{dYv ta% ޑ蕂ZiI "w2oiȱcUa4y׈kִZrK< X,ȊsAN%%(0__R$%'Ĩ1}yym fDn݈kFhNcrrr_رO^}ywVZnZxXmȗdž j۸kn_:0h׌\_@Td$?CP xS]3 Ν;Yh1>,yTΝ;yyh߾ZֳA)9~ѣGkE$qqq{mDm~O>SOӵKWl6s2 iDl5THD=6ty%{WhZ.==ۆFAU@Dn5Es@6aQdZQZߪZ|$ߟJNNI%33)f+.+ Nˮ.\Hb%WFtTT]-\M|J9OD q['ɂw( FGB[>~ lB7Gfa 0  QqwcNV)؋UAUT@DQ@#:ЀfQ%5y{}bsovԛKuKB͕,UEQ];oڿ7k3r~܁ UXQSINI`ŲwprCHLJ"9%訨oۚGJuJ~AY]* xFö OxE/bg/[7ys'9qdWȲSWD*"GOBՀF@BEpZbwQFVeJ;*;MLz^8oN!`9KjTVUt wp, hztPUA]]zp\JpCZJDN F(΅?|DjKޏp57OlLE¯f';':EMpPrBFΞK\bLUl߱%$-;-6oѡ*ٷ'O!6"8q4Mė0o ڵi!(G>̛ 0aR222QTS AZ?BFڴjFҲyYmCŹM[u~(:w;Yt5A8~%;5# viT7~\gvgDmx-YFE4 rreyi8_ȮԘMikĵ>\]wu#33۠8 a1-]/SۋVlɯft'YM5&7YUV;]1SIE,ofA!YF {_'[mvSzMbX_}w_݃*3gٰN?Ns4mGdD$-Yƴh,ݻRY$>¨ӶMk&OzzQOxtS^.<(( K[gGʸ4ƅ eƽ+EG6tYu NVTL6 ?R=@Q%oIFЂH~ZX ֬excw_'iҨ1y5|7WEp&&0+̚63_RVWxw :}-Z}Wu%,?P}Uiii\ Y %O_%/jےƀ 163+DrʝA 0 yh  _U aė0Ln-ݻl6=;7 <"g\, 2vE`ⶾQQZmL{s,~ݛF aѼ9o| ''7ZV_}知WVY*K/I g̙C`eU'j%t45i!&\rܢ/,go?d=w|-Q浧e 8{z]'Ǣ:w]SYM C0̭l*d=5eV/_ƞroKqAf2]zLVVZ) nۑ^Pڕٵ{Km@hX8'OދKn:ƌ~H5'm6]jyoۖsg[]:udᒥdee"cÆ<;ޥ[iڤql榾1mʫx=ɴlقF1gﰲfd")" A-6#t1,AqV'bwX+%YYv=|sOȇ+WyUt/~9:ĤWɣ૗Xb}Y%ܟ# y,Qjݾm+.:?#fg4; ӯ_/2C)`Gr'\t? =sHBCCxkE|h߾Z9qtMk׎ I$%!?/I#!T#9%'O1~8J9(3ODQRfQQ<0>ޜ6/zV-_FDᅗ^ 12|<;)TUe)Փe S_X,qwÇg>^>_ߋ~ҔhӪ%sfLb̝/pl#b"W,D"V@ " *63#~ݍWDxK>%(P)/~K?c{;W_Oyݻ*σEcJ"$$C.ZJIM6㺚E뮉.=d盝a wevLv^!&^:OYjwHOu:xގ-;\lXf}[Vr"w(Tҋ5V+f~Dx8!F\622p! YQa޽tl׮~=qTc[nvq>[r妤 iS;w`SwN b='n֌rj=o[[~SƲo> NGJJ +i%- ~G&66 ҤIgeٳm;i5'DDDThF*v'NuE]γK*WNs\"#+ r+Jn3e{˰۪UT2/oKRu5p}P̲XinrݍYd1 gOӠe^ ACMN׮8{H8{ Q7эZ1ct$Ԣ]P4 pniN%=ߪiH w'4oٖԔdΝ=C5@fMٰa2gNB6gdc0o&;#FN"kvDT t:/Ap.fa6zBѪpٖJPQU:M[o:CUd|h ͛c aѦU+8kW A#uʱxgc2[6d(ХSGwCc8y,F]w'%saCEAЀ ( (všYdejxI(,Uk l<cZU-O$$&1Ȳ̝w gAQSTxzgC!J"("[ `:3WhFTY!4H`/ߣ( &' $:2Sm_e2J׫9CGТ[X hn/[5\_aݑ눊BB4ntm9:-FF1O *l e:~gNq ~tܙl`c._t(7Ӯs7?B AEoF +`X Qa&lˮ?ѥkW4>A(2s|*V;DVy̅Y- >vtT|dXrEb9((RXPdBӡj+$tC1rJcbXzE|Zƌf̙OBc4]ϝVZ8f:W0:o Atו(4kERRt2@Uh( vZtȃ"Ȋ 4"^4zнc e'tRQTk3Uk{At *2?\MbR,cڬټ8joelvC)ZPCluTEA ( H Ig߾hшgkPT~(( bc0x1x[aa!{p-FEFСm|||tkFnTn
    >>̭/EL=vljS1:ϳmִ 8M!72|q,~w9wX*Ma6[.˓_P$Ih5 }/{KLɯ2ʼnvkDQd$%%J~E???~(6ۜ>6TUAŎ&/;^mw( JbTZct/6ʑj],SA Q>ITQeAq1< C~Bz y5DWkV$if_p=37>)70 TX,j5_}-V5z;cC]yKjZv/wY+EF-WnHbR2Y_Ov wwks/DE-[ged1&D_U_54`}R 򸷁/Q8/7ASJd4~?Y%ͻK֭B#iUB /ŋؾm]\\3ӹv_ !<,5EeeW$%з@Ȱ#繑WST"q:3!zSM{08dit1s6Cn֤I~pj{^A볗\Q?-=3 nb-6n†o7J?|u/ʈgFc˫тUR!t:|||Yv-ͷE-@@9ֱ^9NVn&-- VVq( f3| (h^T$]:ubڬٌ2bOy&m䥲'͇޴ 33E/q^h4=n YؤzO[m.ߣ""&#lJ,"_ˆhذ!?d̟܋ӓ5MMӧ/X̍7܈,<+ :*AGDD8۶<|uh5zF=ZA@Qʱu[tf]r 7УG||+$'gHrssرk?n-?6Kdx8?8w/ Ԙl9t#/ƍ degsi##YCLf"-[!"}snzݬicZ4cżx$QdEp"$TļZY%v?OMg@>BbF-FYEQXeKcp0;1_Xs^x$$'Ӻ???QGpAmr33*/๣T- =#90-,xg Z6'((ʋD˔ѣڶVh\=҆_"P CŌNpΡQќ?F-*vxY,= ww#3;-Ã'䝣'Xn}z[UF2n$Γs}ӳŹ%fSP`G\wڙw}dYF$~y[/!2w**Sgff2k{:^̳ru?΂wpx}13x}|WUk܋ww>ޜg#<;AL# Vc=-bȣܱ=cGewuRҷ]YYmZV;v <^?vbVnՂ[\l:>Ur^߭ YJvv.7ma@pԩ6ͷA$ztZnBNVF$I"<"%QM` , o7c4"I%Ttw52e =z\Gxx8QQQ)S%]}A@""*NSb۝:Wk6N_n ]Y/XAX-qEu/^fsA_%XE:]}@%$!ןFNNuҹ <6nO!,a~Sg@@kdg{%ݣox3*tkو &]Yp͗CI"E+eN@U`q@աQ-( [νzAlr с@//Y֥Ktէؔ`b$%ҥiXlTX$aZo_??_IKK/?'>YYYLyu놦zA4DQB(R[TEAnzve]# `ZJy@QuUAv8* : Gj{ya:}:,(Hhbq9P݆,+:4EQxo*?9\PrVceەbcwsL1ZQ18h $oAPrUZr4!ގsRDk#t*F)ZHqcI٬Uk Iqǒ=p7/%ʲOɓ/yZ+U%SD4E;lUbeJa߶8gvyxy?Z=8q~q]BUܜ0͜<}LZhF)Z``Yٽo?:0PQKK{\ݤ(Efv6u\1=4_|(&~eƜ|dgg{\g_|5k?֮q-~l.jy9m&KPs~wXJl}ͭ0fffѸ3.|ͽIE<%u>&ysL&5IPs‘I9,N"לF'fMtY6kҴIz|}+WEa+Oq'jVV\vee*W}(Jt.hMbp. %-5mhf%&Uv1.OlJb^Eps/G],IJkQ-%6Χ&b|2?Ekl+RZHTUbbيHƥ h4v:N>(InEVbm9((6[__RpPrtՆEQoaHIM ܚy^N2`ID+ qP$փVg"HDȦvFBH)EYKrUk p-yPw()c<y6<>^glX6.Ѫe+G)BڶjšÇ(}o vd%FM\26oƫ/_T@VfFѻŠApPBq͛#//sUsJY:4_cM]۰og3O]wrrmDGGɚUEĩ3#APU\Ç>Ol#3+_fLg˓Hdђe9OHH+_HH~h3#Gb F%8l|||6rw,8aa. <pH"LMe֭tzvϾ}h^:])KtܮEFc#wQQQ<Lf3=rs ooD˷ QwEa|܈ݪvڗ>Nzͯ>Ȟ{IINmq~%g'ʶe푲4IA(l=ce?l׭bӿn՟BϑUZ)FA.~i 0_183/AE̜o [pwoG@DAQp lHZ'lG7@ÆȞz9GEUj4ϣMKv%R_aATɵ&ɖ;V,[0>o|jv6.be団b=5jRHh?]fCi٢wslï\Ry $( 4nԨ5`N׎ox7mbOTxou䊂+g /qѲq rrs]:z.yI`4LÌ}~"2UkZl<^yc::(L~a<C% >_슔CTddsG#2wv]{~ڷk[']NѺ*r{sW^y/# 'h۶ ]LzOvv6:o|` 08vZix{{I-ڮ`ڄfRXy| s_q÷ %&vW+F<1񣟤ЪAɫ;LTT`O|Դ Q.s @EĿ(zA?~>"*c}yT,$zC˂{rk"iXnb* As(""(2fUQj.`#ɉJJ hoC9@\j\ YSXOM^a#x> 曷7Uu'^:Ad{:Ͼ^ό.y!? Mf5ivtȨhr:rGGbr[!25kFEK\L*ddD8.I,}oz'8]>ˤ'Iy%g~^C$GOݝDQ VY.Rp7nCvV CP$Mpp0At:]ã(_J^O^^ZkT_CTT.ogxx8Q椪G@>Вt  I2Ν}Yu 8=_?R2gy UֵJ|Lfq(jQb)* ."dU,NMvyy XVLVIlQ$Zk~>n.:!:{X ?4枥]JjƔɬp 0.:roE$N;\Ƚk#™6x޹͚6%$iڞɱx\|||u\i AA!b V5 pM>iF>6 -=+`E6%tX AS2V2ʻF$ ZaOQW YQJ- vdYF˳Ĉqpp5:IQhsֽxP=xyy1|aǷՅAIDATGVd*(Ҫi///O-)jګfu%y,\LٱG17kFYyut:7͛6Q9lؾzyc](!^t}SjǒJS>+(ra 6оm? ,C6G܊[S&M,U^m,ԋd9U~).Y=>\6l$''o|Ϸ[/=0ݪ[\fgdp!-~aF#alc]·/w‚U ]`%,mXWUhYւAB@WTo"Qsb5txC\\qqqBIKO'VR)KX\APы*Z?M/Rn><kl,3g^Cw2?Pzlr㼾}9CTLnIӬi;ڐ҄qL{R۶]u- ёD_?^ryf]NvHH 4$pvu]Tܡffgge{@A Y3ә3%Ki< W+4>g.ÆDauTx=$`(!M]`lDDbXvɥIDӆ$fgR&r7nYf};0o=qR~))*vO.m;?Bqv~k]mG-ksϞ!box[71hs]muKS](1ڪeJ|R:|7p b0W? L=Leo J?ȶctx=`:-Ž(q*h!ΌK]ƫ@[OM|d\,ӊq=IZ'_ '4ƟҪ$*gfIJYFVJ(;5+ֆ-H{~l%'YIENDB`sfwbar-1.0~beta13/.github/workflows/000077500000000000000000000000001450657570000174425ustar00rootroot00000000000000sfwbar-1.0~beta13/.github/workflows/coverity.yml000066400000000000000000000013751450657570000220370ustar00rootroot00000000000000# GitHub actions workflow. # https://docs.github.com/en/actions/learn-github-actions/workflow-syntax-for-github-actions # https://scan.coverity.com/projects/openrc-openrc name: Coverity Scan on: push: branches: [main] jobs: coverity: runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 - run: sudo apt-get update - run: sudo apt-get -y install libgtk-3-dev meson libgtk-layer-shell-dev libjson-c-dev libmpdclient-dev libpulse-dev - run: meson setup builddir/ -Dpulse=enabled -Dmpd=enabled -Dnetwork=enabled env: CC: gcc - uses: vapier/coverity-scan-action@v1 with: command: ninja -C builddir email: ${{ secrets.COVERITY_SCAN_EMAIL }} token: ${{ secrets.COVERITY_SCAN_TOKEN }} sfwbar-1.0~beta13/.github/workflows/freebsd.yml000066400000000000000000000012371450657570000216020ustar00rootroot00000000000000name: FreeBSD_CI on: [push] jobs: test: runs-on: macos-12 name: FreeBSD CI steps: - uses: actions/checkout@v3 - name: Test in FreeBSD id: test uses: vmactions/freebsd-vm@v0 with: usesh: true prepare: | pkg install -y curl meson ninja pkgconf gtk3 wayland-protocols gtk-layer-shell wayland json-c libmpdclient pulseaudio libxkbcommon alsa-lib run: | meson setup build --werror -Dpulse=enabled -Dmpd=enabled -Dnetwork=enabled -Dalsa=enabled -Dbluez=enabled -Dxkb=enabled -Didleinhibit=enabled -Dbsdctl=enabled ninja -C build DESTDIR=./install ninja -C build install sfwbar-1.0~beta13/.github/workflows/main.yml000066400000000000000000000025241450657570000211140ustar00rootroot00000000000000# This is a basic workflow to help you get started with Actions name: CI # Controls when the workflow will run on: # Triggers the workflow on push or pull request events but only for the "main" branch push: branches: [ "main" ] pull_request: branches: [ "main" ] # Allows you to run this workflow manually from the Actions tab workflow_dispatch: # A workflow run is made up of one or more jobs that can run sequentially or in parallel jobs: # This workflow contains a single job called "build" build: # The type of runner that the job will run on runs-on: ubuntu-latest # Steps represent a sequence of tasks that will be executed as part of the job steps: # Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it - uses: actions/checkout@v3 # Runs a set of commands using the runners shell - name: Run a multi-line script run: | sudo apt-get update sudo apt-get -y install libgtk-3-dev meson libgtk-layer-shell-dev libjson-c-dev libmpdclient-dev libpulse-dev bluez libbluetooth-dev libasound2-dev libxkbregistry-dev meson setup build --werror -Dpulse=enabled -Dmpd=enabled -Dnetwork=enabled -Dalsa=enabled -Dbluez=enabled -Dxkb=enabled -Didleinhibit=enabled ninja -C build DESTDIR=./install ninja -C build install sfwbar-1.0~beta13/LICENSE000066400000000000000000001045151450657570000150600ustar00rootroot00000000000000 GNU GENERAL PUBLIC LICENSE Version 3, 29 June 2007 Copyright (C) 2007 Free Software Foundation, Inc. Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The GNU General Public License is a free, copyleft license for software and other kinds of works. The licenses for most software and other practical works are designed to take away your freedom to share and change the works. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change all versions of a program--to make sure it remains free software for all its users. We, the Free Software Foundation, use the GNU General Public License for most of our software; it applies also to any other work released this way by its authors. You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for them if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs, and that you know you can do these things. To protect your rights, we need to prevent others from denying you these rights or asking you to surrender the rights. Therefore, you have certain responsibilities if you distribute copies of the software, or if you modify it: responsibilities to respect the freedom of others. For example, if you distribute copies of such a program, whether gratis or for a fee, you must pass on to the recipients the same freedoms that you received. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. Developers that use the GNU GPL protect your rights with two steps: (1) assert copyright on the software, and (2) offer you this License giving you legal permission to copy, distribute and/or modify it. For the developers' and authors' protection, the GPL clearly explains that there is no warranty for this free software. For both users' and authors' sake, the GPL requires that modified versions be marked as changed, so that their problems will not be attributed erroneously to authors of previous versions. Some devices are designed to deny users access to install or run modified versions of the software inside them, although the manufacturer can do so. This is fundamentally incompatible with the aim of protecting users' freedom to change the software. The systematic pattern of such abuse occurs in the area of products for individuals to use, which is precisely where it is most unacceptable. Therefore, we have designed this version of the GPL to prohibit the practice for those products. If such problems arise substantially in other domains, we stand ready to extend this provision to those domains in future versions of the GPL, as needed to protect the freedom of users. Finally, every program is threatened constantly by software patents. States should not allow patents to restrict development and use of software on general-purpose computers, but in those that do, we wish to avoid the special danger that patents applied to a free program could make it effectively proprietary. To prevent this, the GPL assures that patents cannot be used to render the program non-free. The precise terms and conditions for copying, distribution and modification follow. TERMS AND CONDITIONS 0. Definitions. "This License" refers to version 3 of the GNU General Public License. "Copyright" also means copyright-like laws that apply to other kinds of works, such as semiconductor masks. "The Program" refers to any copyrightable work licensed under this License. Each licensee is addressed as "you". "Licensees" and "recipients" may be individuals or organizations. To "modify" a work means to copy from or adapt all or part of the work in a fashion requiring copyright permission, other than the making of an exact copy. The resulting work is called a "modified version" of the earlier work or a work "based on" the earlier work. A "covered work" means either the unmodified Program or a work based on the Program. To "propagate" a work means to do anything with it that, without permission, would make you directly or secondarily liable for infringement under applicable copyright law, except executing it on a computer or modifying a private copy. Propagation includes copying, distribution (with or without modification), making available to the public, and in some countries other activities as well. To "convey" a work means any kind of propagation that enables other parties to make or receive copies. Mere interaction with a user through a computer network, with no transfer of a copy, is not conveying. An interactive user interface displays "Appropriate Legal Notices" to the extent that it includes a convenient and prominently visible feature that (1) displays an appropriate copyright notice, and (2) tells the user that there is no warranty for the work (except to the extent that warranties are provided), that licensees may convey the work under this License, and how to view a copy of this License. If the interface presents a list of user commands or options, such as a menu, a prominent item in the list meets this criterion. 1. Source Code. The "source code" for a work means the preferred form of the work for making modifications to it. "Object code" means any non-source form of a work. A "Standard Interface" means an interface that either is an official standard defined by a recognized standards body, or, in the case of interfaces specified for a particular programming language, one that is widely used among developers working in that language. The "System Libraries" of an executable work include anything, other than the work as a whole, that (a) is included in the normal form of packaging a Major Component, but which is not part of that Major Component, and (b) serves only to enable use of the work with that Major Component, or to implement a Standard Interface for which an implementation is available to the public in source code form. A "Major Component", in this context, means a major essential component (kernel, window system, and so on) of the specific operating system (if any) on which the executable work runs, or a compiler used to produce the work, or an object code interpreter used to run it. The "Corresponding Source" for a work in object code form means all the source code needed to generate, install, and (for an executable work) run the object code and to modify the work, including scripts to control those activities. However, it does not include the work's System Libraries, or general-purpose tools or generally available free programs which are used unmodified in performing those activities but which are not part of the work. For example, Corresponding Source includes interface definition files associated with source files for the work, and the source code for shared libraries and dynamically linked subprograms that the work is specifically designed to require, such as by intimate data communication or control flow between those subprograms and other parts of the work. The Corresponding Source need not include anything that users can regenerate automatically from other parts of the Corresponding Source. The Corresponding Source for a work in source code form is that same work. 2. Basic Permissions. All rights granted under this License are granted for the term of copyright on the Program, and are irrevocable provided the stated conditions are met. This License explicitly affirms your unlimited permission to run the unmodified Program. The output from running a covered work is covered by this License only if the output, given its content, constitutes a covered work. This License acknowledges your rights of fair use or other equivalent, as provided by copyright law. You may make, run and propagate covered works that you do not convey, without conditions so long as your license otherwise remains in force. You may convey covered works to others for the sole purpose of having them make modifications exclusively for you, or provide you with facilities for running those works, provided that you comply with the terms of this License in conveying all material for which you do not control copyright. Those thus making or running the covered works for you must do so exclusively on your behalf, under your direction and control, on terms that prohibit them from making any copies of your copyrighted material outside their relationship with you. Conveying under any other circumstances is permitted solely under the conditions stated below. Sublicensing is not allowed; section 10 makes it unnecessary. 3. Protecting Users' Legal Rights From Anti-Circumvention Law. No covered work shall be deemed part of an effective technological measure under any applicable law fulfilling obligations under article 11 of the WIPO copyright treaty adopted on 20 December 1996, or similar laws prohibiting or restricting circumvention of such measures. When you convey a covered work, you waive any legal power to forbid circumvention of technological measures to the extent such circumvention is effected by exercising rights under this License with respect to the covered work, and you disclaim any intention to limit operation or modification of the work as a means of enforcing, against the work's users, your or third parties' legal rights to forbid circumvention of technological measures. 4. Conveying Verbatim Copies. You may convey verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice; keep intact all notices stating that this License and any non-permissive terms added in accord with section 7 apply to the code; keep intact all notices of the absence of any warranty; and give all recipients a copy of this License along with the Program. You may charge any price or no price for each copy that you convey, and you may offer support or warranty protection for a fee. 5. Conveying Modified Source Versions. You may convey a work based on the Program, or the modifications to produce it from the Program, in the form of source code under the terms of section 4, provided that you also meet all of these conditions: a) The work must carry prominent notices stating that you modified it, and giving a relevant date. b) The work must carry prominent notices stating that it is released under this License and any conditions added under section 7. This requirement modifies the requirement in section 4 to "keep intact all notices". c) You must license the entire work, as a whole, under this License to anyone who comes into possession of a copy. This License will therefore apply, along with any applicable section 7 additional terms, to the whole of the work, and all its parts, regardless of how they are packaged. This License gives no permission to license the work in any other way, but it does not invalidate such permission if you have separately received it. d) If the work has interactive user interfaces, each must display Appropriate Legal Notices; however, if the Program has interactive interfaces that do not display Appropriate Legal Notices, your work need not make them do so. A compilation of a covered work with other separate and independent works, which are not by their nature extensions of the covered work, and which are not combined with it such as to form a larger program, in or on a volume of a storage or distribution medium, is called an "aggregate" if the compilation and its resulting copyright are not used to limit the access or legal rights of the compilation's users beyond what the individual works permit. Inclusion of a covered work in an aggregate does not cause this License to apply to the other parts of the aggregate. 6. Conveying Non-Source Forms. You may convey a covered work in object code form under the terms of sections 4 and 5, provided that you also convey the machine-readable Corresponding Source under the terms of this License, in one of these ways: a) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by the Corresponding Source fixed on a durable physical medium customarily used for software interchange. b) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by a written offer, valid for at least three years and valid for as long as you offer spare parts or customer support for that product model, to give anyone who possesses the object code either (1) a copy of the Corresponding Source for all the software in the product that is covered by this License, on a durable physical medium customarily used for software interchange, for a price no more than your reasonable cost of physically performing this conveying of source, or (2) access to copy the Corresponding Source from a network server at no charge. c) Convey individual copies of the object code with a copy of the written offer to provide the Corresponding Source. This alternative is allowed only occasionally and noncommercially, and only if you received the object code with such an offer, in accord with subsection 6b. d) Convey the object code by offering access from a designated place (gratis or for a charge), and offer equivalent access to the Corresponding Source in the same way through the same place at no further charge. You need not require recipients to copy the Corresponding Source along with the object code. If the place to copy the object code is a network server, the Corresponding Source may be on a different server (operated by you or a third party) that supports equivalent copying facilities, provided you maintain clear directions next to the object code saying where to find the Corresponding Source. Regardless of what server hosts the Corresponding Source, you remain obligated to ensure that it is available for as long as needed to satisfy these requirements. e) Convey the object code using peer-to-peer transmission, provided you inform other peers where the object code and Corresponding Source of the work are being offered to the general public at no charge under subsection 6d. A separable portion of the object code, whose source code is excluded from the Corresponding Source as a System Library, need not be included in conveying the object code work. A "User Product" is either (1) a "consumer product", which means any tangible personal property which is normally used for personal, family, or household purposes, or (2) anything designed or sold for incorporation into a dwelling. In determining whether a product is a consumer product, doubtful cases shall be resolved in favor of coverage. For a particular product received by a particular user, "normally used" refers to a typical or common use of that class of product, regardless of the status of the particular user or of the way in which the particular user actually uses, or expects or is expected to use, the product. A product is a consumer product regardless of whether the product has substantial commercial, industrial or non-consumer uses, unless such uses represent the only significant mode of use of the product. "Installation Information" for a User Product means any methods, procedures, authorization keys, or other information required to install and execute modified versions of a covered work in that User Product from a modified version of its Corresponding Source. The information must suffice to ensure that the continued functioning of the modified object code is in no case prevented or interfered with solely because modification has been made. If you convey an object code work under this section in, or with, or specifically for use in, a User Product, and the conveying occurs as part of a transaction in which the right of possession and use of the User Product is transferred to the recipient in perpetuity or for a fixed term (regardless of how the transaction is characterized), the Corresponding Source conveyed under this section must be accompanied by the Installation Information. But this requirement does not apply if neither you nor any third party retains the ability to install modified object code on the User Product (for example, the work has been installed in ROM). The requirement to provide Installation Information does not include a requirement to continue to provide support service, warranty, or updates for a work that has been modified or installed by the recipient, or for the User Product in which it has been modified or installed. Access to a network may be denied when the modification itself materially and adversely affects the operation of the network or violates the rules and protocols for communication across the network. Corresponding Source conveyed, and Installation Information provided, in accord with this section must be in a format that is publicly documented (and with an implementation available to the public in source code form), and must require no special password or key for unpacking, reading or copying. 7. Additional Terms. "Additional permissions" are terms that supplement the terms of this License by making exceptions from one or more of its conditions. Additional permissions that are applicable to the entire Program shall be treated as though they were included in this License, to the extent that they are valid under applicable law. If additional permissions apply only to part of the Program, that part may be used separately under those permissions, but the entire Program remains governed by this License without regard to the additional permissions. When you convey a copy of a covered work, you may at your option remove any additional permissions from that copy, or from any part of it. (Additional permissions may be written to require their own removal in certain cases when you modify the work.) You may place additional permissions on material, added by you to a covered work, for which you have or can give appropriate copyright permission. Notwithstanding any other provision of this License, for material you add to a covered work, you may (if authorized by the copyright holders of that material) supplement the terms of this License with terms: a) Disclaiming warranty or limiting liability differently from the terms of sections 15 and 16 of this License; or b) Requiring preservation of specified reasonable legal notices or author attributions in that material or in the Appropriate Legal Notices displayed by works containing it; or c) Prohibiting misrepresentation of the origin of that material, or requiring that modified versions of such material be marked in reasonable ways as different from the original version; or d) Limiting the use for publicity purposes of names of licensors or authors of the material; or e) Declining to grant rights under trademark law for use of some trade names, trademarks, or service marks; or f) Requiring indemnification of licensors and authors of that material by anyone who conveys the material (or modified versions of it) with contractual assumptions of liability to the recipient, for any liability that these contractual assumptions directly impose on those licensors and authors. All other non-permissive additional terms are considered "further restrictions" within the meaning of section 10. If the Program as you received it, or any part of it, contains a notice stating that it is governed by this License along with a term that is a further restriction, you may remove that term. If a license document contains a further restriction but permits relicensing or conveying under this License, you may add to a covered work material governed by the terms of that license document, provided that the further restriction does not survive such relicensing or conveying. If you add terms to a covered work in accord with this section, you must place, in the relevant source files, a statement of the additional terms that apply to those files, or a notice indicating where to find the applicable terms. Additional terms, permissive or non-permissive, may be stated in the form of a separately written license, or stated as exceptions; the above requirements apply either way. 8. Termination. You may not propagate or modify a covered work except as expressly provided under this License. Any attempt otherwise to propagate or modify it is void, and will automatically terminate your rights under this License (including any patent licenses granted under the third paragraph of section 11). However, if you cease all violation of this License, then your license from a particular copyright holder is reinstated (a) provisionally, unless and until the copyright holder explicitly and finally terminates your license, and (b) permanently, if the copyright holder fails to notify you of the violation by some reasonable means prior to 60 days after the cessation. Moreover, your license from a particular copyright holder is reinstated permanently if the copyright holder notifies you of the violation by some reasonable means, this is the first time you have received notice of violation of this License (for any work) from that copyright holder, and you cure the violation prior to 30 days after your receipt of the notice. Termination of your rights under this section does not terminate the licenses of parties who have received copies or rights from you under this License. If your rights have been terminated and not permanently reinstated, you do not qualify to receive new licenses for the same material under section 10. 9. Acceptance Not Required for Having Copies. You are not required to accept this License in order to receive or run a copy of the Program. Ancillary propagation of a covered work occurring solely as a consequence of using peer-to-peer transmission to receive a copy likewise does not require acceptance. However, nothing other than this License grants you permission to propagate or modify any covered work. These actions infringe copyright if you do not accept this License. Therefore, by modifying or propagating a covered work, you indicate your acceptance of this License to do so. 10. Automatic Licensing of Downstream Recipients. Each time you convey a covered work, the recipient automatically receives a license from the original licensors, to run, modify and propagate that work, subject to this License. You are not responsible for enforcing compliance by third parties with this License. An "entity transaction" is a transaction transferring control of an organization, or substantially all assets of one, or subdividing an organization, or merging organizations. If propagation of a covered work results from an entity transaction, each party to that transaction who receives a copy of the work also receives whatever licenses to the work the party's predecessor in interest had or could give under the previous paragraph, plus a right to possession of the Corresponding Source of the work from the predecessor in interest, if the predecessor has it or can get it with reasonable efforts. You may not impose any further restrictions on the exercise of the rights granted or affirmed under this License. For example, you may not impose a license fee, royalty, or other charge for exercise of rights granted under this License, and you may not initiate litigation (including a cross-claim or counterclaim in a lawsuit) alleging that any patent claim is infringed by making, using, selling, offering for sale, or importing the Program or any portion of it. 11. Patents. A "contributor" is a copyright holder who authorizes use under this License of the Program or a work on which the Program is based. The work thus licensed is called the contributor's "contributor version". A contributor's "essential patent claims" are all patent claims owned or controlled by the contributor, whether already acquired or hereafter acquired, that would be infringed by some manner, permitted by this License, of making, using, or selling its contributor version, but do not include claims that would be infringed only as a consequence of further modification of the contributor version. For purposes of this definition, "control" includes the right to grant patent sublicenses in a manner consistent with the requirements of this License. Each contributor grants you a non-exclusive, worldwide, royalty-free patent license under the contributor's essential patent claims, to make, use, sell, offer for sale, import and otherwise run, modify and propagate the contents of its contributor version. In the following three paragraphs, a "patent license" is any express agreement or commitment, however denominated, not to enforce a patent (such as an express permission to practice a patent or covenant not to sue for patent infringement). To "grant" such a patent license to a party means to make such an agreement or commitment not to enforce a patent against the party. If you convey a covered work, knowingly relying on a patent license, and the Corresponding Source of the work is not available for anyone to copy, free of charge and under the terms of this License, through a publicly available network server or other readily accessible means, then you must either (1) cause the Corresponding Source to be so available, or (2) arrange to deprive yourself of the benefit of the patent license for this particular work, or (3) arrange, in a manner consistent with the requirements of this License, to extend the patent license to downstream recipients. "Knowingly relying" means you have actual knowledge that, but for the patent license, your conveying the covered work in a country, or your recipient's use of the covered work in a country, would infringe one or more identifiable patents in that country that you have reason to believe are valid. If, pursuant to or in connection with a single transaction or arrangement, you convey, or propagate by procuring conveyance of, a covered work, and grant a patent license to some of the parties receiving the covered work authorizing them to use, propagate, modify or convey a specific copy of the covered work, then the patent license you grant is automatically extended to all recipients of the covered work and works based on it. A patent license is "discriminatory" if it does not include within the scope of its coverage, prohibits the exercise of, or is conditioned on the non-exercise of one or more of the rights that are specifically granted under this License. You may not convey a covered work if you are a party to an arrangement with a third party that is in the business of distributing software, under which you make payment to the third party based on the extent of your activity of conveying the work, and under which the third party grants, to any of the parties who would receive the covered work from you, a discriminatory patent license (a) in connection with copies of the covered work conveyed by you (or copies made from those copies), or (b) primarily for and in connection with specific products or compilations that contain the covered work, unless you entered into that arrangement, or that patent license was granted, prior to 28 March 2007. Nothing in this License shall be construed as excluding or limiting any implied license or other defenses to infringement that may otherwise be available to you under applicable patent law. 12. No Surrender of Others' Freedom. If conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot convey a covered work so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not convey it at all. For example, if you agree to terms that obligate you to collect a royalty for further conveying from those to whom you convey the Program, the only way you could satisfy both those terms and this License would be to refrain entirely from conveying the Program. 13. Use with the GNU Affero General Public License. Notwithstanding any other provision of this License, you have permission to link or combine any covered work with a work licensed under version 3 of the GNU Affero General Public License into a single combined work, and to convey the resulting work. The terms of this License will continue to apply to the part which is the covered work, but the special requirements of the GNU Affero General Public License, section 13, concerning interaction through a network will apply to the combination as such. 14. Revised Versions of this License. The Free Software Foundation may publish revised and/or new versions of the GNU General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies that a certain numbered version of the GNU General Public License "or any later version" applies to it, you have the option of following the terms and conditions either of that numbered version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of the GNU General Public License, you may choose any version ever published by the Free Software Foundation. If the Program specifies that a proxy can decide which future versions of the GNU General Public License can be used, that proxy's public statement of acceptance of a version permanently authorizes you to choose that version for the Program. Later license versions may give you additional or different permissions. However, no additional obligations are imposed on any author or copyright holder as a result of your choosing to follow a later version. 15. Disclaimer of Warranty. THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 16. Limitation of Liability. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. 17. Interpretation of Sections 15 and 16. If the disclaimer of warranty and limitation of liability provided above cannot be given local legal effect according to their terms, reviewing courts shall apply local law that most closely approximates an absolute waiver of all civil liability in connection with the Program, unless a warranty or assumption of liability accompanies a copy of the Program in return for a fee. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively state the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . Also add information on how to contact you by electronic and paper mail. If the program does terminal interaction, make it output a short notice like this when it starts in an interactive mode: Copyright (C) This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, your program's commands might be different; for a GUI interface, you would use an "about box". You should also get your employer (if you work as a programmer) or school, if any, to sign a "copyright disclaimer" for the program, if necessary. For more information on this, and how to apply and follow the GNU GPL, see . The GNU General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Lesser General Public License instead of this License. But first, please read . sfwbar-1.0~beta13/README.md000066400000000000000000000055071450657570000153330ustar00rootroot00000000000000![](https://github.com/LBCrion/sfwbar/blob/main/.github/sfwbar-dark.png) ![](https://github.com/LBCrion/sfwbar/blob/main/.github/sfwbar.png) ![](https://github.com/LBCrion/sfwbar/blob/main/.github/sfwbar-preview.png) ![](https://github.com/LBCrion/sfwbar/blob/main/.github/sfwbar-mpd.png) ![](https://github.com/LBCrion/sfwbar/blob/main/.github/sfwbar-tray.png) ![](https://scan.coverity.com/projects/22494/badge.svg) ![](https://github.com/LBCrion/sfwbar/actions/workflows/main.yml/badge.svg) ### SFWBar SFWBar (S\* Floating Window Bar) is a flexible taskbar application for wayland compositors, designed with a stacking layout in mind. Originally developed for [Sway](https://github.com/swaywm/sway), SFWBar will work with any wayland compositor supporting layer shell protocol, the taskbar and window switcher functionality shall work with any compositor supportinig foreign toplevel protocol, but the pager, and window placement functionality require sway (or at least i3 IPC support). # If you're getting expression errors when upgrading from version 1.0_beta9 or earlier, please check your data types. The expression parser now applies strict type checks. SFWBar is licensed under GNU GPL. Weather icons are from yr.no and are licensed under MIT license ## SFWBar implements the following features: 1. Taskbar - to control floating windows 1. Task Switcher - to allow switching active window (Alt-Tab) 1. Pager - to allow switching between workspaces 1. Tray - a system tray using status notification item protocol 1. Window placement engine - to open new windows in more logical locations 1. A simple widget set to display information from system files ## Compiling from Source Install dependencies: * gtk3 * gtk-layer-shell * json-c Runtime dependencies: * python is used by some widgets (i.e. battery and start menu widgets) * symbolic icons are used by battery.widget Compile instructions: ```no-highlight meson setup build ninja -C build sudo ninja -C build install ``` ## Configuration Copy sfwbar.config from /usr/share/sfwbar/ to ~/.config/sfwbar/ If you prefer to start with something more like tint2 bar, you can copy [t2.config](config/t2.config) into ~/.config/sfwbar/sfwbar.config instead. If you want something like waybar, you can copy [wbar.config](config/wbar.config) and if you prefer something from the darker side, [w10.config](config/w10.config) could be for you. For more information on the format of configuration file, please see the [man page](doc/sfwbar.rst) If you're using sway, you may want to add the following lines to your sway config file to open windows as floating by default: ```no-highlight # open new windows as floating by default for_window [app_id="[.]*"] floating enable # set Alt-tab as a task switcher combo bindsym Alt+Tab bar hidden_state toggle # set $mod+c to hide/unhide taskbar bindsym $mod+c bar mode toggle ``` sfwbar-1.0~beta13/config/000077500000000000000000000000001450657570000153125ustar00rootroot00000000000000sfwbar-1.0~beta13/config/alsa-module.widget000066400000000000000000000046211450657570000207250ustar00rootroot00000000000000module("alsactl") set XAlsaMutedIcon = If(!Ident($AlsaMutedIcon), "audio-volume-muted-symbolic", $AlsaMutedIcon) set XAlsaLowIcon = If(!Ident($AlsaLowIcon), "audio-volume-low-symbolic", $AlsaLowIcon) set XAlsaMediumIcon = If(!Ident($AlsaMediumIcon), "audio-volume-medium-symbolic", $AlsaMediumIcon) set XAlsaHighIcon = If(!Ident($AlsaHighIcon), "audio-volume-high-symbolic", $AlsaHighIcon) set XAlsaAction = If(!Ident($AlsaAction),"pavucontrol",$AlsaAction) define XAlsaMuted = Alsa("playback-mute") define XAlsaVolume = Alsa("playback-volume") set XAlsaVolumeIcon = Lookup(XAlsaVolume, 80, $XAlsaHighIcon, 50, $XAlsaMediumIcon, 0, $XAlsaLowIcon, $XAlsaMutedIcon) set XAlsaIcon = If(XAlsaMuted,$XAlsaMutedIcon,$XAlsaVolumeIcon) PopUp "XAlsaWindow" { autoclose = true style = "XAlsaPopup" image { value = "value-decrease-symbolic" style = "XAlsaVolumeButton" action[1] = AlsaCmd "playback-volume -1" } scale { style = "alsa_volume_scale" value = XAlsaVolume/100 action[1] = AlsaCmd "playback-volume " + Str(GtkEvent("dir") * 100) trigger = "alsa" } image { value = "value-increase-symbolic" style = "XAlsaVolumeButton" action[1] = AlsaCmd "playback-volume +1" } } layout { style = "module" button { value = $XAlsaIcon trigger = "alsa" tooltip = "Volume: " + Str(XAlsaVolume,0) + "%" + If(XAlsaMuted," (muted)","") style = If(Ident(alsa),"module","hidden") action[Ctrl+LeftClick] = AlsaCmd "playback-mute toggle" action[RightClick] = PopUp "XAlsaWindow" action[ScrollUp] = AlsaCmd "playback-volume +1" action[ScrollDown] = AlsaCmd "playback-volume -1" } } #CSS window#XAlsaWindow { background: rgba(0,0,0,0); -GtkWidget-direction: right; padding: 5px; } grid#XAlsaPopup { margin: 5px; border-radius: 10px; border: 1px solid @borders; padding: 10px; background-color: @theme_bg_color; } image#XAlsaVolumeButton { background-color: rgba(100, 100, 256, 0.3); color: @theme_fg_color; border-radius: 10px; min-width: 20px; min-height: 20px; -GtkWidget-valign: center; -GtkWidget-halign: center; } #alsa_volume_scale { margin-left: 10px; margin-right: 10px; -GtkWidget-direction: right; -GtkWidget-halign: center; -GtkWidget-valign: center; } #alsa_volume_scale trough { min-height: 10px; min-width: 100px; } #alsa_volume_scale progress { min-height: 10px; } sfwbar-1.0~beta13/config/alsa.widget000066400000000000000000000016031450657570000174370ustar00rootroot00000000000000scanner { ExecClient("stdbuf -oL amixer sevents","alsactl") {} Exec("amixer sget Master") { AlsaVolume = RegEx(".*[[](.[0-9]+)%") AlsaMuted = RegEx(".*[[].*[[]([A-Za-z].*)[]]") } } set AlsaMutedIcon = "audio-volume-muted-symbolic" set AlsaVolumeIcon = Lookup(AlsaVolume, 80, "audio-volume-high-symbolic", 50, "audio-volume-medium-symbolic", 0, "audio-volume-low-symbolic", $AlsaMutedIcon) set AlsaIcon = If($AlsaMuted = "off",$AlsaMutedIcon,$AlsaVolumeIcon) layout { style = "module" button { value = $AlsaIcon tooltip = "Volume: " + Str(AlsaVolume,0) + "%" + If(AlsaMuted," (muted)","") style = If(Ident($AlsaVolume),"module","hidden") trigger = "alsactl" action[1] = Exec "amixer sset Master toggle\n" action[3] = Exec $Term + " -e alsamixer" action[4] = Exec "amixer sset Master 5%+\n" action[5] = Exec "amixer sset Master 5%-\n" } } sfwbar-1.0~beta13/config/battery-svg.widget000066400000000000000000000047371450657570000210010ustar00rootroot00000000000000set XBatteryLevel = If(!Ident(BSDCtl),XBatteryLeft / XBatteryTotal * 100,BSDCtl("hw.acpi.battery.level.life")) set XBatterySvgPlus = '' set XBatterySvgMinus = '' set XBatteryDischarging = If(!Ident(BSDCtl),$XBatteryState="Discharging",BSDCtl("hw.acpi.battery.state")="1") set XBatterySvgSign = If( XBatteryDischarging, $XBatterySvgMinus, $XBatterySvgPlus ) set XBatterySvg = ' '+$XBatterySvgSign+' ' define XBatteryInit = "python << END import os sysdir = '/sys/class/power_supply' list = os.scandir(sysdir) batdir='' for entry in list: if(entry.name.startswith('BAT')): batdir = os.path.join(sysdir,entry.name) if(batdir==''): exit(1) list = os.scandir(batdir) print('scanner {') for entry in list: if(entry.name.endswith('_full')): print('file(\"' + os.path.join(batdir,entry.name) + '\") { XBatteryTotal = Grab(First) }') if(entry.name.endswith('_now') and ('charge' in entry.name or 'energy' in entry.name)): print('file(\"' + os.path.join(batdir,entry.name) + '\") { XBatteryLeft = Grab(First) }') if(entry.name == 'status'): print('file(\"' + os.path.join(batdir,entry.name) + '\") { XBatteryState = RegEx(\"^(.*)$\",First) }') print('}') END" layout { style = "module" button "XBatteryModule" { action[0] = PipeRead If(!Ident(BSDCtl),XBatteryInit,"") value = $XBatterySvg style = If(!Ident($XBatteryState) & !Ident(BSDCtl),"hidden","module") tooltip = Str(100*XBatteryLeft/XBatteryTotal) + "%" interval = 1000 } } sfwbar-1.0~beta13/config/battery.widget000066400000000000000000000024021450657570000201670ustar00rootroot00000000000000define BatChargeStr = Str(10*BatLeft/BatTotal,0) define BatIcon = "battery-level-" + BatChargeStr + "0" + If($BatState = "Discharging","", If(BatChargeStr = "10","","-charging")) + "-symbolic" Function("BatteryInitScanner"){ PipeRead "python << END import os sysdir = '/sys/class/power_supply' list = os.scandir(sysdir) batdir='' for entry in list: if(entry.name.startswith('BAT')): batdir = os.path.join(sysdir,entry.name) if(batdir==''): exit(1) list = os.scandir(batdir) print('scanner {') for entry in list: if(entry.name.endswith('_full')): print('file(\"' + os.path.join(batdir,entry.name) + '\") { BatTotal = Grab(First) }') if(entry.name.endswith('_now') and ('charge' in entry.name or 'energy' in entry.name)): print('file(\"' + os.path.join(batdir,entry.name) + '\") { BatLeft = Grab(First) }') if(entry.name == 'status'): print('file(\"' + os.path.join(batdir,'status') + '\") { BatState = RegEx(\"^(.*)$\",First) }') print('}') END" SetValue "battery", BatIcon } layout { style = "module" button "battery" { action[0] = Function "BatteryInitScanner" style = If($BatState="","hidden","module") tooltip = Str(100*BatLeft/BatTotal,0) + "%" value = BatIcon } } sfwbar-1.0~beta13/config/bluez.widget000066400000000000000000000110601450657570000176360ustar00rootroot00000000000000module("bluez") TriggerAction "bluez_updated", Function "XBluezUpdate" TriggerAction "bluez_removed", Function "XBluezRemove" TriggerAction "bluez_scan", SetValue "XBluezScanButton", "Scanning ..." TriggerAction "bluez_scan_complete", SetValue "XBluezScanButton", "Scan" define XBluezID = BluezGet("Path") + "_grid" + If(BluezState("Paired"), "paired", "avail") Menu("XBluezItemMenu") { item("Connect", BluezConnect Extract(WidgetId(),"(.*)_grid") ); item("Disconnect", BluezDisConnect Extract(WidgetId(),"(.*)_grid") ); item("Pair", BluezPair Extract(WidgetId(),"(.*)_grid") ); item("Remove", BluezRemove Extract(WidgetId(),"(.*)_grid") ); } Function("XBluezUpdate") { ClearWidget BluezGet("Path") + "_grid" + If(BluezState("Paired"), "avail", "paired") Config "PopUp 'XBluezWindow' {" + " grid 'XBluezFrame' { grid 'XBluezGrid" + If(BluezState("Paired"),"Paired","Avail") + "' {" + "grid '" + XBluezID + "' {" + "style = 'XBluezGrid'" + "action = BluezPair Extract(WidgetId(),'(.*)_grid') " + "action[3] = Menu 'XBluezItemMenu' " + "image '" + BluezGet("Path") + "_icon' {" + "value = '" + BluezGet("Icon") + "' " + "style = 'XBluezIcon'" + "interval = 0 " + "loc(0,0,1,2)" + "} " + "label '" + BluezGet("Path") + "_label' {" + "value = \"" + BluezGet("Name") + "\"" + "style = 'XBluezName'" + "interval = 0 " + "loc(1,0,2,1)" + "} " + "label '" + BluezGet("Path") +"_status' {" + "value = '" + If(BluezState("Connecting")," Connecting ...","") + "' " + "style = 'XBluezStatus' " + "interval = 0 " + "loc(2,1,1,1)" + "} " + "label '" + BluezGet("Path") +"_address' {" + "value = '" + BluezGet("Address") + "'" + "style = 'XBluezAddress' " + "interval = 0 " + "loc(1,1,1,1)" + "} } } } }" BluezAck } Function("XBluezRemove") { ClearWidget BluezGet("RemovedPath") + "_gridavail" ClearWidget BluezGet("RemovedPath") + "_gridpaired" # ClearWidget BluezGet("RemovedPath") + "_label" # ClearWidget BluezGet("RemovedPath") + "_address" # ClearWidget BluezGet("RemovedPath") + "_icon" BluezAckRemoved } Function("XBluezPopUp") { BluezScan PopUp "XBluezWindow" } Function("XBluezPopDown") { PopUp "XBluezWindow" } Function("XBluezPop") { [!UserState] Function "XBluezPopUp" [UserState] Function "XBluezPopDown" [!UserState] UserState "on" [UserState] UserState "off" } PopUp "XBluezWindow" { style = "XBluezPopup" grid "XBluezFrame" { label { value = "Bluetooth" style = "XBluezHeader" } style = "XBluezFrame" label { value = "Paired devices" style = "XBluezSeparator" } grid "XBluezGridPaired" { style = "XBluezSection" } label { value = "Available devices" style = "XBluezSeparator" } grid "XBluezGridAvail" { style = "XBluezSection" } label "XBluezScanButton" { value = "Scan" style = "XBluezScan" action = BluezScan } } } layout { style = If(BluezState("Running"),"module","hidden") trigger = "bluez_running" button "XBluezWidget" { style = "module" value = "bluetooth-symbolic" action = Function "XBluezPop" } } #CSS window#XBluezWindow { background: rgba(0,0,0,0); -GtkWidget-direction: right; padding: 5px; } grid#XBluezPopup { -GtkWidget-hexpand: true; min-width: 200px; min-height: 300px; margin: 5px; border-radius: 10px; border: 1px solid @borders; padding: 10px; background-color: @theme_bg_color; } grid#XBluezFrame { -GtkWidget-direction: bottom; -GtkWidget-vexpand: true; -GtkWidget-hexpand: true; } grid#XBluezSection { -GtkWidget-hexpand: true; -GtkWidget-direction: bottom; min-height: 20px; } grid#XBluezGrid { padding-top: 3px; padding-bottom: 3px; } image#XBluezIcon { min-height: 32px; min-width: 32px; padding-right: 3px; } label#XBluezHeader { font-size: 20px; padding-bottom: 5px; -GtkWidget-hexpand: true; } label#XBluezName { -GtkWidget-halign: start; } label#XBluezStatus, label#XBluezAddress { font-size: 10px; -GtkWidget-halign: start; -GtkWidget-hexpand: true; } label#XBluezScan { -GtkWidget-vexpand: true; -GtkWidget-hexpand: true; -GtkWidget-valign: end; -GtkLabel-align: 0.5; border-top: dashed 1px @border; padding-top: 3px; } label#XBluezSeparator { padding-top: 3px; font-size: 10px; -GtkWidget-align: 0.0; -GtkWidget-hexpand: true; border-bottom: dashed 1px @border; } sfwbar-1.0~beta13/config/clock.widget000066400000000000000000000006421450657570000176140ustar00rootroot00000000000000layout { style = "frame" label { style = "time" interval = 1000 css = "* { font: 0.5cm Sans; font-weight: 700; -GtkWidget-align: 0.5; -GtkWidget-hexpand: true; -GtkWidget-vexpand: true; }" value = Time("%k:%M") } label { style = "date" interval = 1000 css = "label { -GtkWidget-align: 0.5; -GtkWidget-hexpand: true; -GtkWidget-vexpand: true; }" value = Time("%a %b %d") } } sfwbar-1.0~beta13/config/cpu-temp.source000066400000000000000000000004311450657570000202640ustar00rootroot00000000000000scanner { file("/sys/class/hwmon/hwmon1/temp1_input") { CPUTemp = Grab() } } module("bsdctl") Set BsdCPUTemp = BSDCtl("dev.cpu.0.temperature") Set XCpuTemp = If(!Ident(BSDCtl),CPUTemp,Val(BsdCPUTemp)) Set XCpuTempPresent = If(Ident(BSDCtl),$BsdCPUTemp!="",CPUTemp.count) sfwbar-1.0~beta13/config/cpu-temp.widget000066400000000000000000000004531450657570000202530ustar00rootroot00000000000000include("cpu-temp.source") layout { css = "* { -GtkWidget-direction: right; -GtkWidget-vexpand: true; -GtkWidget-hexpand: true; }" image { style = "value_icon" value = "icons/misc/cpu.svg" } label { style = "value" interval = 1000 value = Str(XCpuTemp/1000,0)+"C" } } sfwbar-1.0~beta13/config/cpu.source000066400000000000000000000017561450657570000173340ustar00rootroot00000000000000# Add up CPU utilization stats across all CPUs scanner { file("/proc/stat") { CpuUser = RegEx("^cpu [\t ]*([0-9]+)",Sum) CpuNice = RegEx("^cpu [\t ]*[0-9]+ ([0-9]+)",Sum) CpuSystem = RegEx("^cpu [\t ]*(?:[0-9]+ ){2}([0-9]+)",Sum) CpuIdle = RegEx("^cpu [\t ]*(?:[0-9]+ ){3}([0-9]+)",Sum) } } module("bsdctl") Set XCpuBSD = BSDCtl("kern.cp_time") Set XCpuUser = If(!Ident(BSDCtl),CpuUser,Extract($XCpuBSD,"([0-9]+)")) Set XCpuSystem = If(!Ident(BSDCtl),CpuSystem,Extract($XCpuBSD,"[0-9]+ ([0-9]+)")) Set XCpuNice = If(!Ident(BSDCtl),CpuNice,Extract($XCpuBSD,"(?:[0-9]+ ){2}([0-9]+)")) Set XCpuIntr = If(!Ident(BSDCtl),0,Extract($XCpuBSD,"(?:[0-9]+ ){3}([0-9]+)")) Set XCpuIdle = If(!Ident(BSDCtl),CpuIdle,Extract($XCpuBSD,"(?:[0-9]+ ){4}([0-9]+)")) Set XCpuUtilization =(XCpuUser-XCpuUser.pval)/ (XCpuUser+XCpuNice+XCpuSystem+XCpuIntr+XCpuIdle- XCpuUser.pval-XCpuNice.pval-XCpuSystem.pval-XCpuIntr.pval-XCpuIdle.pval) Set XCpuPresent = If(Ident(BSDCtl),$XCpuBSD!="",CpuIdle.count) sfwbar-1.0~beta13/config/cpu.widget000066400000000000000000000003541450657570000173100ustar00rootroot00000000000000include("cpu.source") layout { chart { interval = 1000 style = If(XCpuPresent,"cpu_chart","hidden") value = XCpuUtilization tooltip = "CPU: " + Str(XCpuUtilization*100) + '%' action = Exec $Term + " -e top" } } sfwbar-1.0~beta13/config/fan-rpm.widget000066400000000000000000000007461450657570000200660ustar00rootroot00000000000000scanner { # Poll CPU fan rpm file("/sys/devices/platform/it87*/hwmon/hwmon2/fan2_input") { CPUFan = Grab() } # Poll MB fan rpm file("/sys/devices/platform/it87*/hwmon/hwmon2/fan1_input") { MBFan = Grab() } } layout { css = "* { -GtkWidget-direction: right; -GtkWidget-vexpand: true; -GtkWidget-hexpand: true; }" image { style = "value_icon" value = "icons/misc/fan.svg" } label { style = "value" interval = 1000 value = Str(CPUFan,0) } } sfwbar-1.0~beta13/config/idle.widget000066400000000000000000000004341450657570000174350ustar00rootroot00000000000000Module("idleinhibit"); layout { button { style = If(Ident(IdleInhibitState),"module","hidden") value = If(IdleInhibitState()="on", "icons/misc/lock.svg", "icons/misc/unlock.svg") trigger = "idleinhibitor" action[1] = SetIdleInhibitor "toggle" } } sfwbar-1.0~beta13/config/lan-bps.widget000066400000000000000000000010211450657570000200450ustar00rootroot00000000000000scanner { # Add up received and transmitted bytes cross all enp* interfaces file("/proc/net/dev") { EthRcvd = RegEx("wlp.*:[\t ]*([0-9]+)",Sum) EthSent = RegEx("wlp.*:(?:[\t ]*[0-9]+){8}[\t ]([0-9]+)",Sum) } } layout { css = "* { -GtkWidget-direction: right; -GtkWidget-vexpand: true; -GtkWidget-hexpand: true; }" image { style = "value_icon" value = "icons/misc/lan.svg" } label { style = "value" interval = 1000 value = Str((EthRcvd-EthRcvd.pval)/0.001024/EthRcvd.time,0)+"K/s" } } sfwbar-1.0~beta13/config/mb-temp.widget000066400000000000000000000006071450657570000200630ustar00rootroot00000000000000scanner { # Poll motherboard temperature file("/sys/class/hwmon/hwmon2/temp1_input") { MBTemp = Grab(First) } } layout { css = "* { -GtkWidget-direction: right; -GtkWidget-vexpand: true; -GtkWidget-hexpand: true; }" image { style = "value_icon" value = "icons/misc/comp.svg" } label { style = "value" interval = 1000 value = Str(MBTemp/1000,0)+"C" } } sfwbar-1.0~beta13/config/memory.source000066400000000000000000000015051450657570000200450ustar00rootroot00000000000000scanner { file("/proc/meminfo") { MemTotal = RegEx("^MemTotal:[\t ]*([0-9]+)[\t ]") MemFree = RegEx("^MemFree:[\t ]*([0-9]+)[\t ]") MemCache = RegEx( "^Cached:[\t ]*([0-9]+)[\t ]") MemBuff = Regex("^Buffers:[\t ]*([0-9]+)[\t ]") } } module("bsdctl") Set XPageSize = BSDCtl("vm.stats.vm.v_page_size") Set XMemTotal = If(!Ident(BSDCtl),MemTotal,Val(BSDCtl("vm.stats.vm.v_page_count"))*XPageSize) Set XMemFree = If(!Ident(BSDCtl),MemFree,Val(BSDCtl("vm.stats.vm.v_free_count"))*XPageSize) Set XMemCache = If(!Ident(BSDCtl),MemCache,Val(BSDCtl("vm.stats.vm.v_inactive_count"))*XPageSize) Set XMemBuff = If(!Ident(BSDCtl),MemBuff,Val(BSDCtl("vm.stats.vm_laundry_count"))*XPageSize) Set XMemUtilization = (XMemTotal-XMemFree-XMemCache-XMemBuff)/XMemTotal Set XMemPresent = If(Ident(BSDCtl),$XPageSize!="",MemTotal.count) sfwbar-1.0~beta13/config/memory.widget000066400000000000000000000003671450657570000200350ustar00rootroot00000000000000include("memory.source") layout { scale { interval = 1000 style = If(XMemPresent,"memory","hidden") value = XMemUtilization tooltip = "Memory: " + Str(XMemUtilization*100) + "%" action = Exec $Term + " -e top -o %MEM" } } sfwbar-1.0~beta13/config/mpd-intmod.widget000066400000000000000000000057501450657570000205760ustar00rootroot00000000000000include("mpd.source") PopUp "XMpdWindow" { autoclose = false trigger = "mpd" style = If($MpdState="","hidden","XMpdPopup") css = "* { -GtkWidget-direction: bottom; }" label { value = ""+$MpdTitle+""; trigger = "mpd" style = "mpd_title" } label { value = $MpdAlbum; trigger = "mpd" style = "mpd_album" } label { value = $MpdArtist; trigger = "mpd" style = "mpd_artist" } scale { value = (MpdElapsed+If($MpdState="play",MpdElapsed.age/1000000,0))/MpdDuration style = "mpd_progress" action = MpdCmd "seekcur " + Str(gtkevent("x") * MpdDuration) } grid "XMpdPlayer" { css = "* { -GtkWidget-direction: right; -GtkWidget-halign: center; -GtkWidget-hexpand: true; }" image { value = "media-playlist-repeat-symbolic" action = MpdCmd "repeat " + If(MpdRepeat,"0","1") style = If(MpdRepeat,"mpd_icon_red","mpd_icon") trigger = "mpd" } image { value = "media-playlist-shuffle-symbolic" action = MpdCmd "random " + If(MpdRandom,"0","1") style = If(MpdRandom,"mpd_icon_red","mpd_icon") trigger = "mpd" } image { value = "media-seek-backward-symbolic" action = MpdCmd "previous" style = "mpd_icon" } image { value = "media-playback-start-symbolic" action = MpdCmd "play" style = If($MpdState="play","mpd_icon_red","mpd_icon") trigger = "mpd" } image { value = "media-playback-pause-symbolic" action = MpdCmd "pause" style = If($MpdState="pause","mpd_icon_red","mpd_icon") trigger = "mpd" } image { value = "media-playback-stop-symbolic" action = MpdCmd "stop" style = If(mpd("state")="stop","mpd_icon_red","mpd_icon") trigger = "mpd" } image { value = "media-seek-forward-symbolic" action = MpdCmd "next" style = "mpd_icon" } } } layout { style = "module" button { value = "icons/misc/music-player-symbolic" action = PopUp "XMpdWindow" style = If($MpdState="","hidden","module") trigger = "mpd" } } #CSS window#XMpdWindow { background-color: rgba(0,0,0,0); } grid#XMpdPopup { margin: 10px; border-radius: 10px; border-image: none; border-width: 1px; border-style: solid; border-color: @borders; background-color: @theme_bg_color; padding: 10px; } image#mpd_icon { -GtkWidget-hexpand: false; -GtkWidget-vexpand: false; min-width: 16px; min-height: 16px; padding: 4px; color: @theme_fg_color; } image#mpd_icon_red { -GtkWidget-hexpand: false; -GtkWidget-vexpand: false; min-width: 16px; min-height: 16px; padding: 4px; color: red; } label#mpd_title, label#mpd_album, label#mpd_artist { padding-left: 4px; padding-right: 4px; -GtkWidget-align: 0.5; color: @theme_text_color; -GtkWidget-halign: center; } #mpd_progress { margin-top: 3px; margin-bottom: 3px; -GtkWidget-halign: center; } #mpd_progress progress { min-height: 8px; } #mpd_progress trough { min-height: 8px; } sfwbar-1.0~beta13/config/mpd-mini.widget000066400000000000000000000015231450657570000202320ustar00rootroot00000000000000Scanner { MpdClient("") { MpdTitle = RegEx("Title: (.*)") MpdArtist = RegEx("Artist: (.*)") MpdState = RegEx("state: (.*)") } } layout { style = If(MpdState.count=0,"hidden","frame") css = "* { -GtkWidget-direction: right; }" image { style="mpd" value = "icons/misc/rewind.svg" action = MpdCmd "previous" tooltip = $MpdArtist + "\n" + $MpdTitle } image { style="mpd" value = "icons/misc/play.svg" action = MpdCmd "play" tooltip = $MpdArtist + "\n" + $MpdTitle } image { style="mpd" value = "icons/misc/stop.svg" action = MpdCmd "stop" tooltip = $MpdArtist + "\n" + $MpdTitle } image { style="mpd" value = "icons/misc/fforward.svg" action = MpdCmd "next" tooltip = $MpdArtist + "\n" + $MpdTitle } } sfwbar-1.0~beta13/config/mpd-module.widget000066400000000000000000000064771450657570000206000ustar00rootroot00000000000000Module("mpd") Function("XMpdToggleControls") { [!UserState] SetStyle "visible" [UserState] SetStyle "hidden" [!UserState] UserState "on" [UserState] UserState "off" } Function("XMpdToggle") { Function "XMpdPlayer", "XMpdToggleControls" [!UserState] SetValue "pan-end-symbolic" [UserState] SetValue "pan-start-symbolic" [!UserState] UserState "on" [UserState] UserState "off" } PopUp "XMpdWindow" { style = If(!Ident(mpd),"hidden","XMpdPopup") css = "* { -GtkWidget-direction: bottom; }" trigger = "mpd" label { value = ""+Mpd("title")+""; trigger = "mpd" style = "mpd_title" trigger = "mpd" } label { value = Mpd("album"); trigger = "mpd" style = "mpd_album" trigger = "mpd" } label { value = Mpd("artist"); trigger = "mpd" style = "mpd_artist" trigger = "mpd" } scale { value = Val(Mpd("elapsed"))/Val(Mpd("length"))/1000; style = "mpd_progress" trigger = "mpd-progress" } grid "XMpdPlayer" { css = "* { -GtkWidget-direction: right; -GtkWidget-halign: center; -GtkWidget-hexpand: true; }" image { value = "media-playlist-repeat-symbolic" action = MpdCommand "repeat toggle" style = If(val(mpd("repeat")),"mpd_icon_red","mpd_icon") trigger = "mpd" } image { value = "media-playlist-shuffle-symbolic" action = MpdCommand "random toggle" style = If(val(mpd("random")),"mpd_icon_red","mpd_icon") trigger = "mpd" } image { value = "media-seek-backward-symbolic" action = MpdCommand "prev" style = "mpd_icon" } image { value = "media-playback-start-symbolic" action = MpdCommand "play" style = If(mpd("state")="play","mpd_icon_red","mpd_icon") trigger = "mpd" } image { value = "media-playback-pause-symbolic" action = MpdCommand "pause" style = If(mpd("state")="pause","mpd_icon_red","mpd_icon") trigger = "mpd" } image { value = "media-playback-stop-symbolic" action = MpdCommand "stop" style = If(mpd("state")="stop","mpd_icon_red","mpd_icon") trigger = "mpd" } image { value = "media-seek-forward-symbolic" action = MpdCommand "next" style = "mpd_icon" } } } layout { style = "module" button { value = "icons/misc/music-player-symbolic" action = PopUp "XMpdWindow" style = If(!Ident(mpd),"hidden","module") } } #CSS window#XMpdWindow { background-color: rgba(0,0,0,0); } grid#XMpdPopup { margin: 10px; border-radius: 10px; border-image: none; border-width: 1px; border-style: solid; padding: 10px; background-color: @theme_bg_color; } image#mpd_icon { -GtkWidget-hexpand: false; -GtkWidget-vexpand: false; min-width: 16px; min-height: 16px; padding: 4px; color: @theme_fg_color; } image#mpd_icon_red { -GtkWidget-hexpand: false; -GtkWidget-vexpand: false; min-width: 16px; min-height: 16px; padding: 4px; color: red; } label#mpd_title, label#mpd_album, label#mpd_artist { padding-left: 4px; padding-right: 4px; -GtkWidget-align: 0.5; -GtkWidget-halign: center; color: @theme_text_color; } #mpd_progress { margin-top: 3px; margin-bottom: 3px; -GtkWidget-halign: center; } #mpd_progress progress { min-height: 8px; } #mpd_progress trough { min-height: 8px; } sfwbar-1.0~beta13/config/mpd.source000066400000000000000000000007011450657570000173120ustar00rootroot00000000000000Scanner { MpdClient("") { MpdTitle = RegEx("Title: (.*)") MpdAlbum = RegEx("Album: (.*)") MpdArtist = RegEx("Artist: (.*)") MpdState = RegEx("state: (.*)") MpdRandom = RegEx("random:(.*)") MpdRepeat = RegEx("repeat:(.*)") MpdElapsed = RegEx("elapsed:(.*)") MpdDuration = RegEx("duration:(.*)") MpdVolume = RegEx("volume:(.*)") MpdQPos = RegEx("song: (.*)") MpdQLen = RegEx("playlistlength: (.*)") } } sfwbar-1.0~beta13/config/mpd.widget000066400000000000000000000014131450657570000172760ustar00rootroot00000000000000include("mpd.source") layout { style = If(MpdState.count=0,"hidden","frame") label { style = "mpd" interval = 100 value = $MpdTitle loc(1,1,4,1) } label { style = "mpd" interval = 100 value = $MpdArtist loc(1,2,4,1) } image { style="mpd" value = "icons/misc/rewind.svg" loc(1,3,1,1) action = MpdCmd "previous" } image { style="mpd" value = "icons/misc/play.svg" loc(2,3,1,1) action = MpdCmd "play" } image { style="mpd" value = "icons/misc/stop.svg" loc(3,3,1,1) action = MpdCmd "stop" } image { style="mpd" value = "icons/misc/fforward.svg" loc(4,3,1,1) action = MpdCmd "next" } } sfwbar-1.0~beta13/config/network-module.widget000066400000000000000000000050601450657570000214740ustar00rootroot00000000000000module("network") Set XNetworkWifiSvg = ' ' Set XNetworkWirelessIcon = If(Ident(NetworkWirelessIcon),$NetworkWirelessIcon,$XNetworkWifiSvg) Set XNetworkWiredIcon = If(Ident(NetworkWiredIcon),$NetworkWiredIcon,"network-wired-symbolic") Set XNetworkIcon = If(Ident(NetInfo),If(NetInfo("essid")!="",$XNetworkWirelessIcon,$XNetworkWiredIcon),"") layout { style = "module" button { value = $XNetworkIcon trigger = "network" tooltip = "" + "interface: " + Pad(NetInfo("interface"),20) + "\n" + "ip: " + Pad(NetInfo("ip") + "/" + NetInfo("cidr"),20) + "\n" + "netmask: " + Pad(NetInfo("mask"),20) + "\n" + "gateway: " + Pad(NetInfo("gateway"),20) + "\n" + "essid: " + Pad(NetInfo("essid"),20) + "\n" + "signal: " + Pad(Str(NetStat("signal")) + "%",20) + "" style = If(Ident(NetInfo),"module","hidden") } } sfwbar-1.0~beta13/config/oneline.config000066400000000000000000000114141450657570000201330ustar00rootroot00000000000000Set Term="alacritty" function("SfwbarInit") { SetLayer "overlay" SetMonitor "eDP-1" } placer { xstep = 5 # step by 5% of desktop horizontally ystep = 5 # step by 5% of desktop vertically xorigin = 5 yorigin = 5 children = false } # Task Switcher switcher { interval = 700 icons = true labels = false cols = 5 } include("winops.widget") # Panel layout layout { # add a taskbar taskbar { css = "* { -GtkWidget-hexpand: true; }" # take up empty space, set icon size icons = true # display icons labels = true # display titles rows = 1 # stack window buttons across two rows action[3] = Menu "winops" } # add a pager pager { preview = true rows = 1 pins = "1","2","3","4" } # add a launcher button { action = "firefox" # launch firefox on click value = "firefox" # set icon on the button css = "* { min-height: 0.5cm; min-width: 0.5cm; }" # set icon size } button { value = $Term action = Exec $Term css = "* { min-height: 0.5cm; min-width: 0.5cm; }" } include("idle.widget") include("usage.widget") # include("mpd-mini.widget") tray grid { style = "frame" css = "* { -GtkWidget-direction: right; }" include("cpu-temp.widget") include("lan-bps.widget") } include("weather.widget") include("clock.widget") } #CSS button#taskbar_normal image, button#taskbar_active image, button#taskbar_normal:hover image { min-width: 0.4cm; min-height: 0.4cm; } #hidden { -GtkWidget-visible: false; } button#taskbar_normal label, button#taskbar_active label, button#taskbar_normal:hover label { -GtkWidget-vexpand: true; padding-left: 0.75mm; padding-top: 0px; padding-bottom: 0px; font: 0.3cm Sans; } button#taskbar_normal, button#taskbar_active, button#taskbar_normal:hover { padding-left: 0.75mm; padding-top: 0.5mm; padding-bottom: 0.5mm; background-image: none; border-radius: 0; border-image: none; } button#taskbar_active { background-color: #bbddff; } button#taskbar_normal:hover { background-color: #cccccc; } button#pager_normal, button#pager_visible, button#pager_focused { padding-left: 1.25mm; padding-right: 1.25mm; padding-top: 0.5mm; padding-bottom: 0.5mm; background-image: none; border-radius: 0; border-image: none; font: 0.3cm Sans; } button#pager_focused { background-color: #bbddff; } button#pager_preview { background-image: none; border-radius: 0; border-image: none; border-color: #000000; border-width: 0.25mm; color: #777777; min-width: 5cm; min-height: 2.8125cm; } grid#pager { outline-color: #000000; outline-style: dashed; outline-width: 0.25mm; } grid#switcher_active *, grid#switcher_active, grid#switcher_active * * { min-width: 1.25cm; min-height: 1.25cm; background-color: #bbddff; border-image: none; border-radius: 1.25mm; padding: 1.25mm; } grid#switcher_normal *, grid#switcher_normal { min-width: 1.25cm; min-height: 1.25cm; border-image: none; padding: 1.25mm; } window#switcher { border-style: solid; border-width: 0.25mm; border-color: #000000; border-radius: 1.25mm; padding: 1.25mm; } grid#switcher { border-radius: 1.25mm; padding: 1.25mm; } button#tray_active, button#tray_passive, button#tray_attention { background-image: none; border: 0px; padding: 0px; margin: 0px; border-image: none; border-radius: 0px; outline-style: none; box-shadow: none; } button#tray_active image, button#tray_passive image, button#tray_attention image { min-width: 7mm; min-height: 7mm; padding: 1mm; } progressbar { padding-left: 0.25mm; padding-right: 0.25mm; -GtkWidget-vexpand: true; -GtkWidget-direction: top; } progress, trough { border-radius: 0; border-color: #9c9c9c; } progress { border-style: outset; min-width: 2mm; } trough { background-color: #a1a1a1; border-style: inset; min-height: 2.5mm; min-width: 2.5mm; } label#time, label#date { min-width: 1.6cm; } grid#frame { -GtkWidget-direction: right; min-width: 2cm; border-color: #9c9c9c; border-style: groove; border-width: 0.5mm; padding-top: 0.25mm; } grid#layout { padding: 0.25mm; -GtkWidget-direction: right; } image#label { padding: 0.1cm; } label#value { min-width: 1cm; -GtkWidget-align: 0; } image#value_icon { min-width: 0.8cm; min-height: 0.8cm; padding: 0.1cm; padding-top: 1.5mm; padding-bottom: 1.5mm; } image#mpd { min-width: 0.4cm; min-height: 0.4cm; padding-top: 2mm; padding-bottom: 2mm; padding-left: 1mm; padding-right: 1mm; } button#module { -GtkWidget-vexpand: true; } label { font: 0.27cm Sans; color: #000000; } tooltip label { color: #cccccc; } label#awesome { font-family: "Font Awesome 5 Free"; font-weight: 900; } sfwbar-1.0~beta13/config/pulse-module.widget000066400000000000000000000050311450657570000211310ustar00rootroot00000000000000module("pulsectl") set XPulseMutedIcon = If(!Ident($PulseMutedIcon), "audio-volume-muted-symbolic", $PulseMutedIcon) set XPulseLowIcon = If(!Ident($PulseLowIcon), "audio-volume-low-symbolic", $PulseLowIcon) set XPulseMediumIcon = If(!Ident($PulseMediumIcon), "audio-volume-medium-symbolic", $PulseMediumIcon) set XPulseHighIcon = If(!Ident($PulseHighIcon), "audio-volume-high-symbolic", $PulseHighIcon) set XPulseAction = If(!Ident($PulseAction),"pavucontrol",$PulseAction) define XPulseMuted = Val(Pulse("sink-mute")) define XPulseVolume = Val(Pulse("sink-volume")) set XPulseVolumeIcon = Lookup(XPulseVolume, 80, $XPulseHighIcon, 50, $XPulseMediumIcon, 0, $XPulseLowIcon, $XPulseMutedIcon) set XPulseIcon = If(XPulseMuted,$XPulseMutedIcon,$XPulseVolumeIcon) PopUp "XPulseWindow" { style = "XPulsePopup" image { value = "value-decrease-symbolic" style = "XPulseVolumeButton" action[1] = PulseCmd "sink-volume -1" } scale { style = "pulse_volume_scale" value = XPulseVolume/100 action[1] = PulseCmd "sink-volume " + Str(GtkEvent("dir") * 100) trigger = "pulse" } image { value = "value-increase-symbolic" style = "XPulseVolumeButton" action[1] = PulseCmd "sink-volume +1" } } layout { style = "module" button { value = $XPulseIcon trigger = "pulse" tooltip = "Volume: " + Str(XPulseVolume,0) + "%" + If(XPulseMuted," (muted)","") + "\nForm: " + Pulse("sink-form") + "\nPort: " + Pulse("sink-port") + "\nMonitor: " + Pulse("sink-monitor") style = If(Ident(pulse),"module","hidden") action[1] = PulseCmd "sink-mute toggle" action[2] = Exec $XPulseAction action[3] = PopUp "XPulseWindow" action[4] = PulseCmd "sink-volume +1" action[5] = PulseCmd "sink-volume -1" } } #CSS window#XPulseWindow { background: rgba(0,0,0,0); -GtkWidget-direction: right; padding: 5px; } grid#XPulsePopup { margin: 5px; border-radius: 10px; border: 1px solid @borders; padding: 10px; background-color: @theme_bg_color; } image#XPulseVolumeButton { background-color: rgba(100, 100, 256, 0.3); color: @theme_fg_color; border-radius: 10px; min-width: 20px; min-height: 20px; -GtkWidget-valign: center; -GtkWidget-halign: center; } #pulse_volume_scale { margin-left: 10px; margin-right: 10px; -GtkWidget-direction: right; -GtkWidget-halign: center; -GtkWidget-valign: center; } #pulse_volume_scale trough { min-height: 10px; min-width: 100px; } #pulse_volume_scale progress { min-height: 10px; } sfwbar-1.0~beta13/config/rfkill-bt.widget000066400000000000000000000007001450657570000204020ustar00rootroot00000000000000include("rfkill.source") layout { button { style = If(!XRfKillBTPresent, "hidden","module") value = If(XRfKillBTSoft = 1, "bluetooth-disabled-symbolic", "bluetooth-active-symbolic") tooltip = "Bluetooth: Soft" + If(XRfKillBTSoft,"","Un")+"blocked" + ", Hard" + If(XRfKillBTHard,"","Un")+"blocked" trigger = "rfkill" action = Exec "rfkill " + If(XRfKillBTSoft,"un","") + "block bluetooth" } } sfwbar-1.0~beta13/config/rfkill-wifi.widget000066400000000000000000000007311450657570000207370ustar00rootroot00000000000000include("rfkill.source") layout { button { style = If(!XRfKillWifiPresent, "hidden","module") value = If(XRfKillWifiSoft = 1, "network-wireless-hardware-disabled-symbolic", "network-wireless-connected-symbolic") tooltip = "WiFi: Soft" + If(XRfKillWifiSoft,"","Un")+"blocked" + ", Hard" + If(XRfKillWifiHard,"","Un")+"blocked" trigger = "rfkill" action = Exec "rfkill " + If(XRfKillWifiSoft,"un","") + "block wifi" } } sfwbar-1.0~beta13/config/rfkill.source000066400000000000000000000015751450657570000200270ustar00rootroot00000000000000scanner { ExecClient("stdbuf -oL rfkill event","rfkill") { RfkillWifiSoft = RegEx("type 1 op [0-9]+ soft ([0-9]+)") RfkillWifiHard = RegEx("type 1 op [0-9]+ soft [0-9]+ hard ([0-9]+)") RfkillBluetoothSoft = RegEx("type 2 op [0-9]+ soft ([0-9]+)") RfkillBluetoothHard = RegEx("type 2 op [0-9]+ soft [0-9]+ hard ([0-9]+)") } } Set XRfKillBTPresent = Cached(XRfKillBTPresent) | RfKillBluetoothSoft.count Set XRfKillBTSoft = If(RfKillBluetoothSoft.count, RfKillBluetoothSoft, Cached(XRfKillBTSoft)) Set XRfKillBTHard = If(RfKillBluetoothHard.count, RfKillBluetoothHard, Cached(XRfKillBTHard)) Set XRfKillWifiPresent = Cached(XRfKillWifiPresent) | RfKillWifiSoft.count Set XRfKillWifiSoft = If(RfKillWifiSoft.count, RfKillWifiSoft, Cached(XRfKillWifiSoft)) Set XRfKillWifiHard = If(RfKillWifiHard.count, RfKillWifiHard, Cached(XRfKillWifiHard)) sfwbar-1.0~beta13/config/sfwbar.config000066400000000000000000000127531450657570000177750ustar00rootroot00000000000000# Override a gtk theme # theme = "Adwaita-dark" # Display minimized windows on taskbars across all outputs # DisownMinimized = true # Select terminal emulator to use Set Term = "alacritty" TriggerAction "SIGRTMIN+1", SwitcherEvent "forward" TriggerAction "SIGRTMIN+2", SwitcherEvent "back" Function("SfwbarInit") { SetLayer "top" SetBarSize "100%" SetMirror "*" # SetBarSensor "1000" # SetExclusiveZone "0" } # Window Placer placer { xorigin = 5 # place the first window at X% from the left yorigin = 5 # place the first window at X% from the top xstep = 5 # step by X% of desktop horizontally ystep = 5 # step by X% of desktop vertically children = true } # Task Switcher switcher { interval = 700 icons = true labels = false cols = 5 } include("winops.widget") layout { css = "* { min-height: 20px }" include("startmenu.widget") taskbar { style = "taskbar" rows = 1; icons = true; labels = true; sort = false; action[3] = Menu "winops" } label { css = "* { -GtkWidget-hexpand: true; }" } pager { style = "pager" rows = 1 pins = "1","2","3","4" preview = true } include("cpu.widget") include("memory.widget") tray { rows = 1 } include("battery-svg.widget") include("bluez.widget") # include("idle.widget") # include("rfkill-wifi.widget") # include("rfkill-bt.widget") include("network-module.widget") # include("alsa-module.widget") include("pulse-module.widget") include("mpd-intmod.widget") include("sway-lang.widget") grid { css = "* { -GtkWidget-direction: bottom; }" label { value = Time("%k:%M") style ="clock" } label { value = Time("%x") style ="clock" } } } #CSS #hidden { -GtkWidget-visible: false; } button, button image { outline-style: none; box-shadow: none; background-image: none; border-image: none; } button#startmenu { border: none; } label { font: 14px Sans; } button#module { border: none; padding: 3px; margin: 0px; -GtkWidget-vexpand: true; } button#module image { min-height: 16px; min-width: 16px; padding: 0px; margin: 0px; -GtkWidget-valign: center; -GtkWidget-vexpand: true; } button#taskbar_normal, button#taskbar_active, button#taskbar_normal:hover { border-radius: 3px; border-width: 0px; -GtkWidget-hexpand: false; } grid#taskbar > :not(:last-child) button#taskbar_normal:hover, grid#taskbar > :not(:last-child) button#taskbar_active, grid#taskbar > :not(:last-child) button#taskbar_normal { border-radius: 0px; border-right: 1px solid alpha(@theme_fg_color,0.15); } button#taskbar_active { background-color: rgba(199,227,255,0.6); } button#taskbar_normal:hover { background-color: rgba(213,213,213,0.6); } button#taskbar_normal grid, button#taskbar_active grid, button#taskbar_normal:hover grid { -GtkWidget-valign: center; } button#taskbar_normal image, button#taskbar_active image, button#taskbar_normal:hover image { min-width: 24px; min-height: 24px; padding-right: 6px; } button#taskbar_normal label, button#taskbar_active label, button#taskbar_normal:hover label { } button#tray_active, button#tray_passive, button#tray_attention { margin: 0px; border: none; padding: 0px; } button#tray_active image, button#tray_passive image, button#tray_attention image { -GtkWidget-valign: center; -GtkWidget-vexpand: true; min-height: 16px; min-width: 16px; padding: 3px; margin: 0px; border: none; } grid#pager { border-radius: 3px; border-color: @theme_fg_color; border-style: solid; border-width: 1px; margin: 2px; padding: 0px; } button#pager_normal, button#pager_visible, button#pager_focused { border-radius: 5px; border: none; outline: 0px; margin-right: 2px; margin-left: 2px; padding: 0px; font: 0.3cm Sans; -GtkWidget-halign: center; -GtkWidget-valign: center; } button#pager_focused { background-color: rgba(199,227,255,0.6); } button#pager_preview { border-radius: 0; border-color: #000000; border-width: 0.25mm; color: #777777; min-width: 5cm; min-height: 2.8125cm; } #menu_item, #menu_item image, #menu_item label { -GtkWidget-halign: start; } #menu_item image { min-width: 16px; min-height: 16px; padding-right: 2px; } label#clock { padding-left: 2px; padding-right: 2px; } chart#cpu_chart { background: rgba(127,127,127,0.3); min-width: 9px; -GtkWidget-vexpand: true; margin: 2px; border: 1px solid @theme_fg_color; color: red; } progressbar#memory { -GtkWidget-direction: top; -GtkWidget-vexpand: true; min-width: 9px; border: 1px solid @theme_fg_color; margin: 2px; } progressbar#memory trough { min-height: 2px; min-width: 9px; border: none; border-radius: 0px; background: rgba(127,127,127,0.3); } progressbar#memory progress { -GtkWidget-hexpand: true; min-width: 9px; border-radius: 0px; border: none; margin: 0px; background-color: alpha(green,0.9); } grid#switcher_active image, grid#switcher_active { min-width: 50px; min-height: 50px; padding: 5px; background-color: #bbddff; border-radius: 5px; -GtkWidget-hexpand: true; } grid#switcher_normal image, grid#switcher_normal { min-width: 50px; min-height: 50px; padding: 5px; -GtkWidget-direction: right; -GtkWidget-hexpand: true; } window#switcher { border: 1px solid @borders; border-radius: 6px; padding: 60px; -GtkWidget-hexpand: true; } grid#switcher { border-radius: 5px; padding: 5px; -GtkWidget-hexpand: true; } label#sway_lang { margin-left: 3px; margin-right: 5px; } sfwbar-1.0~beta13/config/showdesktop.widget000066400000000000000000000025711450657570000210760ustar00rootroot00000000000000function("ShowDesktopSaveFocus") { [Focused] UserState "2:on" [!Focused] UserState "2:off" } function("ShowdesktopMinimize") { [!Minimized] UserState "on" [Minimized] UserState "off" [!Minimized] Minimize } function("ShowDesktopRestore") { [UserState] UnMinimize } function("ShowDesktopRestoreFocus") { [UserState2] Focus } function("ShowDesktopCheckWindow") { [!Minimized] UserState "target","2:on" } function("ShowDesktopReminimize") { [!Minimized] UserState "on" [!Minimized] Minimize } function("ShowDesktopUnminimized") { [UserState2 | Children] Function "ShowDesktopSaveFocus" [UserState2 | Children] Function "ShowDesktopReminimize" [!UserState2 | Children] Function "ShowDesktopRestore" [!UserState2 | Children] Function "ShowDesktopRestoreFocus" [!UserState2] UserState "off" [UserState2] UserState "on" } function("ShowDesktop") { [!UserState | Children] Function "ShowDesktopSaveFocus" [!UserState | Children] Function "ShowDesktopMinimize" [UserState] UserState "2:off" [UserState| Children ] Function "ShowDesktopCheckWindow" [UserState] Function "target","ShowDesktopUnminimized" [!UserState] UserState "on" } layout { button { value = "user-desktop" style = "showdesktop" tooltip = "Show Desktop" action = Function "target", "ShowDesktop" } taskbar "target" { css = "* { -GtkWidget-visible: false; }" } } sfwbar-1.0~beta13/config/startmenu.widget000066400000000000000000000060531450657570000205450ustar00rootroot00000000000000Function("StartMenu") { PipeRead "python << END import string, sys, fileinput, getopt, os, glob def main(): # Use categories from XDG spec catmap = { 'Development': 'Development', 'Education': 'Education', 'Game': 'Games', 'Graphics': 'Graphics', 'Internet': 'Internet', 'Settings': 'Settings', 'Screensaver': 'Settings', 'System': 'System', 'Emulator': 'System', 'Network': 'Internet', 'AudioVideo': 'Multimedia', 'Audio': 'Multimedia', 'Video': 'Multimedia', 'Utility': 'Accessories', 'Accessibility': 'Accessories', 'Core': 'Accessories', 'Office': 'Office'} cats = list() outp = list() print ('MenuClear(\\'Menugen_Applications\\')') dirlist = os.environ.get('XDG_DATA_DIRS') if not dirlist: dirlist = '/usr/share' dhome = os.environ.get('XDG_DATA_HOME') if not dhome: dhome = os.environ.get('HOME') if dhome: dhome = dhome + '/.local/share' if dhome: dirlist = dirlist + ':' + dhome dlist = [] for dir in dirlist.split(':'): dlist = dlist + sorted(glob.glob(dir + '/applications/*.desktop')) for dfile in dlist: de_name = de_icon = de_exec = de_cat = de_disp = '' fchan = open(dfile) for dline in fchan.readlines(): try: (tag, value) = dline.strip().split('=',1) if tag.strip() == 'Name' and not de_name: # Grab first name de_name = value.strip() if tag.strip() == 'Icon': de_icon = value.strip() if tag.strip() == 'Exec': de_exec = value.strip() if tag.strip() == 'Categories': de_cat = value.strip() if tag.strip() == 'NoDisplay': de_disp = value.strip() except: pass fchan.close() if (de_name != '') and (de_exec != '') and (de_disp.lower() != 'true'): de_cat = map_category ( catmap, de_cat ) de_exec = de_exec.replace(' %f','').replace(' %F','').replace(' %u','') de_exec = de_exec.replace(' %U','').replace(' %c',' '+de_name) de_exec = de_exec.replace(' %k',' '+dfile).replace(' %i',' '+de_icon) if not (de_cat in cats): cats.append(de_cat) print ('MenuClear(\\'Menugen_' + de_cat + '\\')') sub_icon = '%applications-' + de_cat.lower() if (de_cat.lower() == 'settings'): # these is no applications-settings icon sub_icon = '%preferences-system' outp.append('Menu(\\'Menugen_Applications\\') { SubMenu(\\'' + de_cat + sub_icon + '\\',\\'Menugen_' + de_cat + '\\') }') outp.append('Menu(\\'Menugen_' + de_cat + '\\') { Item(\\'' + de_name + '%' + de_icon + '\\',Exec \\'' + de_exec +'\\') }') for token in sorted(outp): print(token) def map_category ( catmap, catlist ): for cat in catlist.strip().split(';'): try: mycat = catmap[cat] except LookupError: mycat = '' if mycat != '': return mycat return 'Other' if __name__ == '__main__': main() END" Menu "MenuGen_Applications" } layout { button { value = "open-menu-symbolic" style = "startmenu" action = Function "StartMenu" tooltip = "Application menu" } } sfwbar-1.0~beta13/config/swap.source000066400000000000000000000003061450657570000175050ustar00rootroot00000000000000Scanner { # Add up total and used swap capacity across all partitions file("/proc/swaps") { SwapTotal = RegEx("[\t ]([0-9]+)",Sum) SwapUsed = RegEx("[\t ][0-9]+[\t ]([0-9]+)",Sum) } } sfwbar-1.0~beta13/config/sway-lang.widget000066400000000000000000000013351450657570000204230ustar00rootroot00000000000000module("xkbmap") scanner { SwayClient("") { SwayXkbLayout = Json(".input.input.xkb_active_layout_name") } } Set XSwayLangCommand = If(Ident($SwayLangCommand),$SwayLangCommand, "input type:keyboard xkb_switch_layout next") Function("XSwayLangInit") { SwayCmd "input type:keyboard xkb_switch_layout next" SwayCmd "input type:keyboard xkb_switch_layout prev" } layout { label { action[0] = Function "XSwayLangInit" action[1] = SwayCmd $XSwayLangCommand style = If(Ident(SwayXkbLayout) & $SwayXkbLayout!="","sway_lang","hidden") value = Upper(XkbMap($SwayXkbLayout,"description","name")) trigger = "sway" } } #CSS label#sway_lang { -GtkWidget-vexpand: true; -GtWidget-valign: center; } sfwbar-1.0~beta13/config/t2.config000066400000000000000000000134131450657570000170300ustar00rootroot00000000000000Set Term = "alacritty" # Window Placer placer { xorigin = 5 # place the first window at X% from the left yorigin = 5 # place the first window at X% from the top xstep = 5 # step by X% of desktop horizontally ystep = 5 # step by X% of desktop vertically children = true } # Task Switcher switcher { interval = 700 icons = true labels = false cols = 5 } function("SfwbarInit") { SetBarId "bar-0" SetLayer "top" } include("winops.widget") # Panel layout layout { include("startmenu.widget") button { style = "launcher" value = $Term action = Exec $Term } button { style = "launcher" value = "firefox" action = "firefox" } pager { style = "pager" rows = 1 preview = true numeric = true } taskbar { rows = 1 css = "* { -GtkWidget-hexpand: true; }" # stretch horizontally icons = true labels = true action[3] = Menu "winops" action[2] = Close } include("cpu.widget") include("memory.widget") tray { rows = 1 } include("battery-svg.widget") include("bluez.widget") include("network-module.widget") # Select pulse-module or alsa-module based on your sound configuration include("pulse-module.widget") # include("alsa-module.widget") include("mpd-intmod.widget") include("sway-lang.widget") grid { css = "* { padding-left: 5px; padding-right: 5px; }" label { value = Time("%k:%M") loc(1,1) } label { value = Time("%A %e %B") loc(1,2) } } } #CSS @define-color theme_text_color white; @define-color theme_bg_color black; @define-color theme_fg_color white; @define-color borders rgba(256,256,256,0.3); window#sfwbar { -GtkWidget-direction: top; background-color: rgba(0,0,0,0.6); } grid#layout { -GtkWidget-direction: right; min-height: 30px; } grid { padding: 0px; margin: 0px; } tooltip { background-color: @theme_bg_color; } image { min-width: 18px; min-height: 18px; box-shadow: none; border: none; border-image: none; background-image: none; background: none; -gtk-icon-shadow: none; } button { box-shadow: none; border-image: none; background-image: none; background: none; outline-style: none; } menu { background-color: black; border: 1px solid @borders; padding: 3px; } menu arrow { background-color: rgba(0,0,0,0); color: @theme_text_color; } menuitem { color: @theme_text_color; padding: 2px; } menu image { padding-right: 5px; } progressbar trough { background-color: rgba(256, 256, 256, 0.5); border-color: rgba(256,256,256,0.5); } #hidden { -GtkWidget-visible: false; } button#taskbar_normal grid { -GtkWidget-hexpand: false; } button#taskbar_normal image, button#taskbar_active image, button#taskbar_normal:hover image { min-width: 24px; min-height: 24px; } button#taskbar_normal label, button#taskbar_active label, button#taskbar_normal:hover label { padding: 0px 0px 0px 5px; -GtkWidget-hexpand: false; } button#taskbar_normal, button#taskbar_active, button#taskbar_normal:hover { padding: 0px 5px 0px 5px; border-radius: 4px; background-color: rgba(119,119,119,0.2); border-color: rgba(119,119,119,0.3); -GtkWidget-hexpand: false; } button#taskbar_active { background-color: rgba(255,255,255,0.2); border-color: rgba(255,255,255,0.4); } button#taskbar_normal:hover, button#taskbar_active:hover { background-color: rgba(176,176,176,0.22); border-color: rgba(234,234,234,0.44); } button#pager_normal, button#pager_visible, button#pager_focused { padding: 0px 5px 0px 5px; border-radius: 4px; background-color: rgba(119,119,119,0); border-color: rgba(119,119,119,0); } button#pager_focused { background-color: rgba(119,119,119,0.2); border: none; } button#pager_preview { border-radius: 0; border-color: #000000; border-width: 0.25mm; color: #777777; min-width: 5cm; min-height: 2.8125cm; } grid#switcher_active image, grid#switcher_active { min-width: 60px; min-height: 60px; border-image: none; padding: 6px; background-color: #777777; border: 0px; box-shadow: none; border-radius: 1.25mm; -GtkWidget-hexpand: true; } grid#switcher_normal image, grid#switcher_normal { min-width: 60px; min-height: 60px; padding: 6px; -GtkWidget-direction: right; -GtkWidget-hexpand: true; } window#switcher { background-color: rgba(0,0,0,0); padding: 6px; -GtkWidget-hexpand: true; } grid#switcher { border-radius: 6px; background-color: @theme_bg_color; border: 1px solid @borders; padding: 6px; box-shadow: none; -GtkWidget-hexpand: true; } button#tray_active, button#tray_passive, button#tray_attention { padding: 0px 1px 0px 0px; border: none; margin: 0px; -GtkWidget-valign: center; } button#startmenu image, button#module image{ -ScaleImage-color: @theme_text_color; } button#module, button#launcher, button#showdesktop, button#startmenu { padding: 0px 3px 0px 3px; border: none; -GtkWidget-valign: center; } button#startmenu { border: 1px solid; border-radius: 4px; } button#startmenu image { min-width: 18px; min-height: 18px; padding: 1px; } chart#cpu_chart { background: rgba(127,127,127,0.3); min-width: 9px; margin: 2px; border: 1px solid @borders; color: red; } progressbar#memory { -GtkWidget-direction: top; min-width: 9px; border: 1px solid @borders; margin: 2px; } progressbar#memory trough { min-height: 2px; min-width: 9px; border: none; border-radius: 0px; background: rgba(127,127,127,0.3); } progressbar#memory progress { -GtkWidget-hexpand: true; min-width: 9px; border-radius: 0px; border: none; margin: 0px; background-color: alpha(green,0.9); } label#sway_lang { margin-left: 3px; margin-right: 5px; } label { font: 12px Sans; color: @theme_text_color; text-shadow: none; } * { -GtkWidget-vexpand: true; } sfwbar-1.0~beta13/config/test.config000066400000000000000000000125711450657570000174660ustar00rootroot00000000000000 include("winops.widget") function("SfwbarInit") { SetLayer "panel", "bottom" SetMonitor "panel", "DVI-D-1" SetExclusiveZone "panel", "35" SetLayer "launcher", "overlay" SetBarSensor "launcher", "1000" SetMonitor "launcher", "DVI-D-1" SetExclusiveZone "launcher", "0" SetBarSize "launcher", "300" } # work arounds for vivaldi/opera icons .. there could be more #MapAppId ".*Vivaldi$", "vivaldi" MapAppId ".*Opera$", "opera" # Panel layout layout "panel" { style = frame #include("buttonmenu.widget") #include("menu.widget") #include("memory.widget") #include("barlauncher.widget") #include("showdesktop.widget") #include("desktop_goto.widget") #include("desktop_mv.widget") taskbar { rows = 1 css = "* { -GtkWidget-hexpand: true; }" # stretch horizontally sort = false group = false icons = true labels = true action[3] = Menu "winops" } #include("includes.widget") label { style = "time" interval = 1000 tooltip = Time("%a %b %d") css = "* { font: 11pt Mono; font-weight: 600; -GtkWidget-align: 0.5; -GtkWidget-hexpand: true; -GtkWidget-vexpand: true; min-width: 80px; padding-right: 4px; }" value = Time("%k:%M %Z") } } # Launcher layout #layout "launcher" { include("launcher.widget") } # /Launcher layout layout "launcher" { css = "* { -GtkWidget-direction: right; -GtkWidget-visible: true; }" button { value = "directory-home" action = "thunar" tooltip = "File Manager" css = "* { min-height: 36px; min-width: 36px; margin: 4px; }" } button { value = "browser" action = "firefox" tooltip = "Browser" css = "* { min-height: 36px; min-width: 36px; margin: 4px; }" } button { value = "terminal" action = "lxterminal" tooltip = "Terminal" css = "* { min-height: 36px; min-width: 36px; margin: 4px; }" } button { value = "edit" action = "geany" tooltip = "Text Editor" css = "* { min-height: 36px; min-width: 36px; margin: 4px; }" } button { value = "mail" action = "claws-mail" tooltip = "Email" css = "* { min-height: 36px; min-width: 36px; margin: 4px; }" } button { value = "calendar" action = "osmo" tooltip = "Calendar" css = "* { min-height: 36px; min-width: 36px; margin: 4px; }" } button { value = "paint" action = "mtpaint" tooltip = "Paint" css = "* { min-height: 36px; min-width: 36px; margin: 4px; }" } } #CSS window#panel { -GtkWidget-direction: top; border-radius: 6px; background-color: alpha(@theme_bg_color, 0.5); } window#launcher { -GtkWidget-direction: bottom ; } button#pager_normal , button#pager_visible , button#pager_focused { background-color: rgba(0, 0, 0, 0.0); padding-left: 1.25mm; padding-right: 1.25mm; padding-top: 0.5mm; padding-bottom: 0.5mm; background-image: none; border-radius: 0; border-image: none; font: 11pt Sans; } button#pager_focused { background-color: #bbddff; } button#pager_preview { background-color: rgba(0, 0, 0, 0.0); background-image: none; border-radius: 0; border-image: none; border-color: #000000; border-width: 0.25mm; color: #777777; min-width: 5cm; min-height: 1.4125cm; } button#menu { background-color: transparent; border: 1px solid transparent; padding: 3px; } grid#pager { outline-color: #000000; outline-style: dashed; outline-width: 0.25mm; } grid#switcher_active *, grid#switcher_active, grid#switcher_active * * { min-width: 0.5cm; min-height: 0.5cm; border-image: none; padding: 1.25mm; background-color: #bbddff; border-radius: 1.25mm; } grid#switcher_normal *, grid#switcher_normal, grid#switcher_normal * * { min-width: 0.5cm; min-height: 0.5cm; border-image: none; padding: 0.5mm; } window#switcher { border-style: solid; border-width: 0.25mm; border-color: #000000; border-radius: 1.25mm; padding: 1.25mm; -GtkWidget-hexpand: true; } grid#switcher { border-radius: 1.25mm; padding: 1.25mm; } image#tray_active, image#tray_passive, image#tray_attention { min-width: 1cm; min-height: 1cm; } button#taskbar_normal image, button#taskbar_active image, button#taskbar_normal:hover image { background-color: rgba(0, 0, 0, 0.0); min-width: 20px; min-height: 20px; } button#taskbar_normal label, button#taskbar_active label, button#taskbar_normal:hover label { background-color: rgba(0, 0, 0, 0.0); -GtkWidget-vexpand: true; padding-left: 0.5mm; padding-top: 0px; padding-bottom: 0px; font: 11pt Sans; } button#taskbar_normal , button#taskbar_active , button#taskbar_normal:hover { padding: 3px; border: 0.5px; } button#taskbar_normal:hover { background-color: #BF892B; } button#taskbar_group_normal image , button#taskbar_group_active image { min-width: 20px; min-height: 20px; } grid#frame { -GtkWidget-direction: bottom; min-width: 1cm; border-color: #9c9c9c; border-style: groove; border-width: 0.5mm; padding-top: 0.25mm; } grid#layout { padding: 0.25mm; -GtkWidget-direction: right; } label#value { min-width: 1cm; -GtkWidget-hexpand: true; -GtkWidget-align: 0.5; } image#value_icon { padding: 0.1cm; min-width: 16px; min-height: 16px; } button { padding: 3px; border-width: 0.5px; border-radius: 6px; } button#startmenu { background-image: none; border: 0px; border-image: none; border-radius: 6px; outline-style: none; box-shadow: none; min-height: 24px; min-width: 24px; } sfwbar-1.0~beta13/config/test.widget000066400000000000000000000004321450657570000174750ustar00rootroot00000000000000scanner { Exec("./switchuser icon") { SwitchUserIcon = RegEx(".*(.*)") SwitchUserTool = RegEx(".*(.*)") } } layout { button { value = $SwitchUserIcon style = "app" action = Exec "./switchuser" tooltip = $SwitchUserTool } } sfwbar-1.0~beta13/config/test2.config000066400000000000000000000146311450657570000175470ustar00rootroot00000000000000# Override a gtk theme #theme = "Adwaita-dark" # Display minimized windows on taskbars across all outputs DisownMinimized = false # Select terminal emulator to use Set Term = "gnome-terminal" # Window Placer - Only works with sway placer { xorigin = 5 # place the first window at X% from the left yorigin = 5 # place the first window at X% from the top xstep = 5 # step by X% of desktop horizontally ystep = 5 # step by X% of desktop vertically children = true } # Task Switcher - move through open windows with switcher { interval = 700 icons = true labels = false cols = 5 } # Panel Layout layout { #include("startmenu.widget") # add a launcher button { action = "firefox" # launch firefox on click value = "firefox" # set icon on the button tooltip = "Firefox Web Browser" css = "* { min-height: .75cm; min-width: .75cm; }" # set icon size style = "launcher" } button { action = "gnome-terminal --title=standard" value = "gnome-terminal" tooltip = $Term css = "* { min-height: .75cm; min-width: .75cm; }" style = "launcher" } button { action = "thunar" value = "thunar" tooltip = "Thunar File Manager" css = "* { min-height: .75cm; min-width: .75cm; }" style = "launcher" } button { action = "thunderbird" value = "thunderbird" tooltip = "Mail Client" css = "* { min-height: .75cm; min-width: .75cm; }" style = "launcher" } button { action = "chromium --no-sandbox -ozone-platform-hint=auto" value = "chromium" tooltip = " Chromium Web Browser" css = "* { min-height: .75cm; min-width: .75cm; }" style = "launcher" } button { action = "play" value = "/usr/share/icons/Adwaita_old/24x24/legacy/media-playback-start.png" tooltip = "Play Song Library" css = "* { min-height: .75cm; min-width: .75cm; }" style = "launcher" } # add a taskbar #css = "* { min-height: 50px }" include("winops.widget") #taskbar { #css = "* { -GtkWidget-hexpand: true; }" # take up empty space, set icon size #css = "* { padding-left: 10px; padding-right: 800px }" #style = "taskbar" #rows = 1; #icons = true; #labels = true; #sort = false; #filter = output #title_width = 25 #action[3] = Menu "winops" #} #Pager - switch between workspaces. only works with sway # pager { # style = "pager" # rows = 1 # pins = "1","2","3","4" # preview = true # } tray { rows = 1 } include("pulse-module.widget") include("network-module.widget") #label { #css = "* { padding-left: 5px; }" #} #grid { #css = "* { -GtkWidget-direction: bottom; }" #label { #value = Time("%k:%M") #style ="clock" #} #label { #value = Time("%x") #style ="clock" #} #} } #CSS #hidden { -GtkWidget-visible: false; } window { background-color: rgba(0.033,0.041,0.047,0.7); } button#startmenu { border: none; } button#module { border: none; padding: 3px; margin: 0px; background: none; -GtkWidget-vexpand: true; } button#launcher { padding: 0px 5px 0px 5px; background: none; border-style:none; box-shadow: none; } button#module image { min-height: 24px; min-width: 24px; padding: 0px; margin: 0px; background: none; -GtkWidget-valign: center; -GtkWidget-vexpand: true; } button#taskbar_normal, button#taskbar_active, button#taskbar_normal:hover { outline-style: none; box-shadow: none; background-image: none; border-radius: 3px; border-image: none; border-width: 0px; -GtkWidget-hexpand: false; } grid#taskbar > :not(:last-child) button#taskbar_normal:hover, grid#taskbar > :not(:last-child) button#taskbar_active, grid#taskbar > :not(:last-child) button#taskbar_normal { border-radius: 0px; border-right: 1px solid alpha(@theme_fg_color,0.15); } button#taskbar_active { background-color: rgba(199,227,255,0.6); } button#taskbar_normal:hover { background-color: rgba(213,213,213,0.6); } button#taskbar_normal grid, button#taskbar_active grid, button#taskbar_normal:hover grid { -GtkWidget-valign: center; } button#taskbar_normal image, button#taskbar_active image, button#taskbar_normal:hover image { min-width: 24px; min-height: 24px; padding-right: 6px; } button#taskbar_normal label, button#taskbar_active label, button#taskbar_normal:hover label { } button#tray_active, button#tray_passive, button#tray_attention { margin: 0px; border: none; padding: 0px; } button#tray_active image, button#tray_passive image, button#tray_attention image { outline-style: none; box-shadow: none; -GtkWidget-valign: center; -GtkWidget-vexpand: true; min-height: 24px; min-width: 24px; padding: 3px; margin: 0px; border: none; } grid#pager { border-radius: 3px; border-color: @theme_fg_color; border-style: solid; border-width: 1px; margin: 2px; padding: 0px; } button#pager_normal, button#pager_visible, button#pager_focused { outline-style: none; box-shadow: none; background-image: none; border-radius: 5px; border-image: none; border: none; outline: 0px; margin-right: 2px; margin-left: 2px; padding: 0px; font: 0.3cm Sans; -GtkWidget-halign: center; -GtkWidget-valign: center; } button#pager_focused { background-color: rgba(199,227,255,0.6); } button#pager_preview { background-image: none; border-radius: 0; border-image: none; border-color: #000000; border-width: 0.25mm; color: #777777; min-width: 5cm; min-height: 2.8125cm; } #menu_item, #menu_item image, #menu_item label { -GtkWidget-halign: start; } #menu_item image { min-width: 16px; min-height: 16px; padding-right: 2px; } label#clock { padding-left: 2px; padding-right: 2px; } label { font: 14px Sans; } chart#cpu_chart { background: rgba(127,127,127,0.3); min-width: 9px; -GtkWidget-vexpand: true; margin: 2px; border: 1px solid @theme_fg_color; color: red; } progressbar#memory { -GtkWidget-direction: top; -GtkWidget-vexpand: true; min-width: 9px; border: 1px solid @theme_fg_color; margin: 2px; } progressbar#memory trough { min-height: 2px; min-width: 9px; border: none; border-radius: 0px; background: rgba(127,127,127,0.3); } progressbar#memory progress { -GtkWidget-hexpand: true; min-width: 9px; border-radius: 0px; border: none; margin: 0px; background-color: alpha(green,0.9); } grid#switcher_active image, grid#switcher_active { min-width: 1.25cm; min-height: 1.25cm; border-image: none; padding: 1.25mm; background-color: #bbddff; border-radius: 1.25mm; -GtkWidget-hexpand: true; } grid#switcher_normal image, grid#switcher_normal { min-width: 1.25cm; min-height: 1.25cm; border-image: none; padding: 1.25mm; -GtkWidget-direction: right; -GtkWidget-hexpand: true; } window#switcher { border-style: solid; border-width: 0.25mm; border-color: #000000; border-radius: 1.25mm; padding: 1.25mm; -GtkWidget-hexpand: true; } grid#switcher { border-radius: 1.25mm; padding: 1.25mm; -GtkWidget-hexpand: true; } sfwbar-1.0~beta13/config/twoline.config000066400000000000000000000161411450657570000201650ustar00rootroot00000000000000Set Term = "alacritty" scanner { # Extract memory usage information file("/proc/meminfo") { MemTotal = RegEx("^MemTotal:[\t ]*([0-9]+)[\t ]") MemFree = RegEx("^MemFree:[\t ]*([0-9]+)[\t ]") MemCache = RegEx( "^Cached:[\t ]*([0-9]+)[\t ]") MemBuff = Regex("^Buffers:[\t ]*([0-9]+)[\t ]") } # Add up total and used swap capacity across all partitions file("/proc/swaps") { SwapTotal = RegEx("[\t ]([0-9]+)",Sum) SwapUsed = RegEx("[\t ][0-9]+[\t ]([0-9]+)",Sum) } # Add up CPU utilization stats across all CPUs file("/proc/stat") { CpuUser = RegEx("^cpu [\t ]*([0-9]+)",Sum) CpuNice = RegEx("^cpu [\t ]*[0-9]+ ([0-9]+)",Sum) CpuSystem = RegEx("^cpu [\t ]*[0-9]+ [0-9]+ ([0-9]+)",Sum) CpuIdle = RegEx("^cpu [\t ]*[0-9]+ [0-9]+ [0-9]+ ([0-9]+)",Sum) } # Get total and remaining battery charge file("/sys/class/power_supply/BAT0/charge_full") { BatteryTotal = Grab(Sum) } file("/sys/class/power_supply/BAT0/charge_now") { BatteryLeft = Grab(Sum) } file("/sys/class/power_supply/AC/online") { ACOnline = Grab(Sum) } } # Window Placer placer { xorigin = 5 # place the first window at X% from the left yorigin = 5 # place the first window at X% from the top xstep = 5 # step by X% of desktop horizontally ystep = 5 # step by X% of desktop vertically children = true } # Task Switcher switcher { interval = 700 icons = true labels = false cols = 5 } function("SfwbarInit") { SetBarId "bar-0" # SetLayer "bottom" # SetMonitor "eDP-1" # SetBarSize "800" } include("winops.widget") # Panel layout layout { taskbar { rows = 2 css = "* { -GtkWidget-hexpand: true; }" # stretch horizontally icons = true labels = true action[3] = Menu "winops" } # label { css = "* { -GtkWidget-hexpand: true; }" } # add a pager pager { style = "pager" rows = 2 preview = true numeric = true pins = "1","2","3","4" } # add a launcher button { value = "firefox" action = "firefox" # launch firefox on click css = "* { min-height: 1.25cm; min-width: 1.25cm; }" # set icon size } button { value = $Term action = Exec $Term css = "* { min-width: 1.25cm; min-height: 1.25cm; }"; } include("idle.widget") grid { scale { interval = 1000 css = "progressbar progress { background-color: #0000ff;}" action[1] = Exec $Term + " -e top" value = (CpuUser-CpuUser.pval)/(CpuUser+CpuNice+CpuSystem+CpuIdle -CpuUser.pval-CpuNice.pval-CpuSystem.pval-CpuIdle.pval) tooltip = Str((CpuUser-CpuUser.pval)/(CpuUser+CpuNice+CpuSystem+CpuIdle -CpuUser.pval-CpuNice.pval-CpuSystem.pval-CpuIdle.pval)*100,0)+'%' } scale { interval = 1000 css = "progressbar progress { background-color: #00ff00;}" value= (MemTotal-MemFree-MemCache-MemBuff)/MemTotal } scale { interval = 1000 style = ACOnline css = "progressbar#1 progress { background-color: #ff0000;} \ progressbar#0 progress { background-color: #ffff00;}" value = BatteryLeft/BatteryTotal } } include("mpd.widget") tray { rows = 2 } grid { style = "frame" css = "* { -GtkWidget-direction: right; }" label { loc(1,1) } include("cpu-temp.widget") label { loc(1,2) } include("lan-bps.widget") label { loc(1,3) } include("mb-temp.widget") label { loc(1,4) } include("fan-rpm.widget") } include("weather.widget") include("clock.widget") } #CSS window { -GtkWidget-direction: bottom; } #hidden { -GtkWidget-visible: false; } button#taskbar_normal grid { -GtkWidget-hexpand: false; padding-right: 0px; margin-right: 0px; } button#taskbar_normal image, button#taskbar_active image, button#taskbar_normal:hover image { min-width: 0.4cm; min-height: 0.4cm; } button#taskbar_normal label, button#taskbar_active label, button#taskbar_normal:hover label { -GtkWidget-vexpand: true; -GtkWidget-hexpand: false; padding-left: 0.75mm; padding-top: 0px; padding-bottom: 0px; font: 0.3cm Sans; } button#taskbar_normal , button#taskbar_active , button#taskbar_normal:hover { padding-left: 0.75mm; padding-top: 0.5mm; padding-bottom: 0.5mm; background-image: none; border-radius: 0; border-image: none; -GtkWidget-hexpand: false; } button#taskbar_active { background-color: #bbddff; } button#taskbar_normal:hover { background-color: #cccccc; } button#pager_normal , button#pager_visible , button#pager_focused { padding-left: 1.25mm; padding-right: 1.25mm; padding-top: 0.5mm; padding-bottom: 0.5mm; background-image: none; border-radius: 0; border-image: none; font: 0.3cm Sans; } button#pager_focused { background-color: #bbddff; } button#pager_preview { background-image: none; border-radius: 0; border-image: none; border-color: #000000; border-width: 0.25mm; color: #777777; min-width: 5cm; min-height: 2.8125cm; } grid#pager { outline-color: #000000; outline-style: dashed; outline-width: 0.25mm; } grid#switcher_active image, grid#switcher_active { min-width: 1.25cm; min-height: 1.25cm; border-image: none; padding: 1.25mm; background-color: #bbddff; border-radius: 1.25mm; -GtkWidget-hexpand: true; } grid#switcher_normal image, grid#switcher_normal { min-width: 1.25cm; min-height: 1.25cm; border-image: none; padding: 1.25mm; -GtkWidget-direction: right; -GtkWidget-hexpand: true; } window#switcher { border-style: solid; border-width: 0.25mm; border-color: #000000; border-radius: 1.25mm; padding: 1.25mm; -GtkWidget-hexpand: true; } grid#switcher { border-radius: 1.25mm; padding: 1.25mm; -GtkWidget-hexpand: true; } button#tray_active, button#tray_passive, button#tray_attention { background-image: none; border: 0px; padding: 0px; margin: 0px; border-image: none; border-radius: 0px; outline-style: none; box-shadow: none; } button#tray_active image, button#tray_passive image, button#tray_attention image { min-width: 0.43cm; min-height: 0.43cm; padding: 1mm; } progressbar { padding-left: 0.25mm; padding-right: 0.25mm; -GtkWidget-vexpand: true; -GtkWidget-direction: top; } progress, trough { border-radius: 0; border-color: #9c9c9c; } progress { border-style: outset; min-width: 2mm; } trough { background-color: #a1a1a1; border-style: inset; min-height: 2.5mm; min-width: 2.5mm; } grid#frame { -GtkWidget-direction: bottom; min-width: 2cm; border-color: #9c9c9c; border-style: groove; border-width: 0.5mm; padding-top: 0.25mm; } grid#layout { padding: 0.25mm; -GtkWidget-direction: right; } label#value { -GtkWidget-hexpand: true; -GtkWidget-align: 1; padding-right: 2mm; padding-left: 2mm; } image#value_icon { min-width: 0.3cm; min-height: 0.3cm; } label#mpd { padding-top: 1mm; padding-bottom: 1mm; padding-left: 2mm; padding-right: 2mm; } image#mpd { min-width: 0.4cm; min-height: 0.4cm; padding-left: 0.5mm; padding-right: 0.5mm; } button#module { -GtkWidget-vexpand: true; } label { font: 0.27cm Sans; } label#awesome { font-family: "Font Awesome 5 Free"; font-weight: 900; } sfwbar-1.0~beta13/config/usage.widget000066400000000000000000000015761450657570000176340ustar00rootroot00000000000000include("cpu.source") include("memory.source") scanner { # Get total and remaining battery charge file("/sys/class/power_supply/BAT0/charge_full") { BatteryTotal = Grab(Sum) } file("/sys/class/power_supply/BAT0/charge_now") { BatteryLeft = Grab(Sum) } file("/sys/class/power_supply/AC/online") { ACOnline = Grab(Sum) } } layout { # add a sub-grid to house scales style = "scales" scale { if(XCpuPresent,"","hidden") css = "progressbar progress { background-color: #0000ff;}" value = XCpuUtilization } scale { if(XMemoryPresent,"","hidden") css = "progressbar progress { background-color: #00ff00;}" value= XMemoryUtilization } scale { value = BatteryLeft/BatteryTotal style = ACOnline css = "progressbar#1 progress { background-color: #ff0000;} \ progressbar#0 progress { background-color: #ffff00;}" } } sfwbar-1.0~beta13/config/vertical.config000066400000000000000000000115241450657570000203150ustar00rootroot00000000000000Set Term="alacritty" function("SfwbarInit") { SetLayer "overlay" SetMonitor "eDP-1" } include("winops.widget") placer { xstep = 5 # step by 5% of desktop horizontally ystep = 5 # step by 5% of desktop vertically xorigin = 5 yorigin = 5 children = false } # Task Switcher switcher { interval = 700 icons = true labels = false cols = 5 } # Panel layout layout { # add a taskbar taskbar { css = "* { -GtkWidget-vexpand: false; }" # take up empty space, set icon size icons = true # display icons labels = false # don't display titles cols = 1 # stack window buttons across two rows action[3] = Menu "winops" } label { css = "* { -GtkWidget-vexpand: true; }" } # add a pager pager { preview = true cols = 2 pins = "1","2","3","4" } # add a launcher button { action = "firefox" # launch firefox on click value = "firefox" # set icon on the button css = "* { min-height: 1.25cm; min-width: 1.25cm; }" # set icon size } button { action = Exec $Term value = $Term css = "* { min-height: 1.25cm; min-width: 1.25cm; }" } include("idle.widget") # include("mpd-mini.widget") { # css = "* { min-height: 0.1cm; }" # } include("usage.widget") tray grid { style = "frame" include("cpu-temp.widget") include("lan-bps.widget") include("mb-temp.widget") include("fan-rpm.widget") } include("weather.widget") include("clock.widget") } #CSS window { -GtkWidget-direction: left; } #hidden { -GtkWidget-visible: false; } button#taskbar_normal *, button#taskbar_active *, button#taskbar_normal:hover * { min-height: 1.25cm; -GtkWidget-hexpand: true; } button#taskbar_normal label, button#taskbar_active label, button#taskbar_normal:hover label { min-height: 1.25cm; padding: 0px; font: 0.3cm Sans; } button#taskbar_normal , button#taskbar_active , button#taskbar_normal:hover { min-height: 1.25cm; padding: 0.5mm; background-image: none; border-radius: 0; border-image: none; } button#taskbar_active { background-color: #bbddff; } button#taskbar_normal:hover { background-color: #cccccc; } button#pager_normal , button#pager_visible , button#pager_focused { -GtkWidget-hexpand: true; padding-left: 1.25mm; padding-right: 1.25mm; padding-top: 0.5mm; padding-bottom: 0.5mm; background-image: none; border-radius: 0; border-image: none; font: 0.3cm Sans; } button#pager_focused { background-color: #bbddff; } button#pager_preview { background-image: none; border-radius: 0; border-image: none; border-color: #000000; border-width: 0.25mm; color: #777777; min-width: 5cm; min-height: 2.8125cm; } grid#pager { outline-color: #000000; outline-style: dashed; outline-width: 0.25mm; } grid#switcher_active *, grid#switcher_active, grid#switcher_active * * { min-width: 1.25cm; min-height: 1.25cm; background-color: #bbddff; border-image: none; border-radius: 1.25mm; padding: 1.25mm; } grid#switcher_normal *, grid#switcher_normal { min-width: 1.25cm; min-height: 1.25cm; border-image: none; padding: 1.25mm; } window#switcher { border-style: solid; border-width: 0.25mm; border-color: #000000; border-radius: 1.25mm; padding: 1.25mm; } grid#switcher { border-radius: 1.25mm; padding: 1.25mm; } button#tray_active, button#tray_passive, button#tray_attention { background-image: none; border: 0px; padding: 0px; margin: 0px; border-image: none; border-radius: 0px; outline-style: none; box-shadow: none; -GtkWidget-direction: bottom; } button#tray_active image, button#tray_passive image, button#tray_attention image { min-width: 7mm; min-height: 7mm; padding: 1mm; } progressbar { padding-left: 0.25mm; padding-right: 0.25mm; -GtkWidget-hexpand: true; -GtkWidget-direction: right; } progress, trough { border-radius: 0; border-color: #9c9c9c; } progress { border-style: outset; min-height: 2mm; } trough { background-color: #a1a1a1; border-style: inset; min-height: 2.5mm; min-width: 2.5mm; } grid#scales { -GtkWidget-direction: bottom; } grid#frame { -GtkWidget-direction: bottom; -GtkWidget-vexpand: false; min-height: 1.5cm; border-color: #9c9c9c; border-style: groove; border-width: 0.5mm; padding-top: 0.25mm; } grid#layout { padding: 0.25mm; -GtkWidget-direction: bottom; } label#value { -GtkWidget-hexpand: true; -GtkWidget-align: 1; padding-right: 2mm; padding-left: 2mm; } image#value_icon { min-width: 0.25cm; min-height: 0.25cm; } image#mpd { min-width: 0.4cm; min-height: 0.4cm; padding: 0.5mm; } button#module { -GtkWidget-hexpand: true; } label { font: 0.27cm Sans; color: #000000; } tooltip label { color: #cccccc; } label#awesome { font-family: "Font Awesome 5 Free"; font-weight: 900; } sfwbar-1.0~beta13/config/w10.config000066400000000000000000000130301450657570000171050ustar00rootroot00000000000000Set Term = "alacritty" # Window Placer placer { xorigin = 5 # place the first window at X% from the left yorigin = 5 # place the first window at X% from the top xstep = 5 # step by X% of desktop horizontally ystep = 5 # step by X% of desktop vertically children = true } # Task Switcher switcher { interval = 700 icons = true labels = false cols = 5 } function("SfwbarInit") { SetBarId "bar-0" SetLayer "top" SetExclusiveZone "-1" SetMonitor "eDP-1" # SetBarSize "bar1","800" } include("winops.widget") # Panel layout layout "sfwbar" { include("startmenu.widget") include("showdesktop.widget") taskbar { rows = 1 group = true group cols = 1 group style = "taskbar_group" group labels = true group icons = true icons = true labels = false action[3] = Menu "winops" } label { css = "* { -GtkWidget-hexpand: true; min-height: 28px; }" } tray { rows = 1 } include("battery-svg.widget") include("bluez.widget") # include("rfkill-wifi.widget") # include("rfkill-bt.widget") include("network-module.widget") # Select pulse-module or alsa-module based on your sound configuration include("pulse-module.widget") # include("alsa-module.widget") include("mpd-intmod.widget") include("sway-lang.widget") grid { css = "* { -GtkWidget-direction: bottom; padding: 5px; padding-left: 0px; }" label { value = Time("%k:%M") style ="clock" } label { value = Time("%x") style ="clock" } } } #CSS @define-color theme_bg_color #353231; @define-color theme_fg_color #d1d1d1; @define-color theme_text_color #d1d1d1; @define-color borders #777777; window { -GtkWidget-direction: bottom; background-color: #353231; } label { font: 12px sans; color: @theme_text_color; text-shadow: none; } button, button image { outline-style: none; box-shadow: none; background-image: none; border-image: none; } window#taskbar_group { background-color: RGBA(0.2,0.2,0.2,0.5); } #hidden { -GtkWidget-visible: false; } button#taskbar_group_normal, button#taskbar_group_normal:hover, button#taskbar_group_active { margin: 0px; border: 0px; border-radius: 0px; -GtkWidget-valign: center; } button#taskbar_group_normal image, button#taskbar_group_normal:hover image, button#taskbar_group_active image { min-height: 30px; min-width: 30px; margin: 0px; } button#taskbar_group_active { background-color: #493B41; } button#taskbar_normal:hover, button#taskbar_group_normal:hover, button#taskbar_group_active:hover { background-color: #535353; } button#taskbar_normal grid { padding-right: 0px; margin-right: 0px; min-height: 0px; } button#taskbar_normal image, button#taskbar_active image, button#taskbar_normal:hover image { min-width: 18px; min-height: 18px; } button#taskbar_normal label, button#taskbar_active label, button#taskbar_normal:hover label { -GtkWidget-hexpand: true; -GtkWidget-halign: start; padding: 0px 7px 0px 0px; } button#taskbar_normal, button#taskbar_active, button#taskbar_normal:hover { border-radius: 0; border: 0px; box-shadow: none; -GtkWidget-hexpand: false; -GtkWidget-vexpand: false; min-height: 0px; background-color: #535353; } button#taskbar_active { background-color: #646464; } button#taskbar_normal:hover { background-color: #646464; } #menu_item, #menu_item *, #menu_item image, #menu_item label { -GtkWidget-halign: start; color: white; } #menu_item image { min-width: 16px; min-height: 16px; padding-right: 2px; } menu { background-color: #353231; } menu arrow { background: none; } button#pager_normal, button#pager_visible, button#pager_focused { padding: 0px 5px 0px 5px; border-radius: 0; } button#pager_focused { background-color: #bbddff; } button#pager_preview { border-radius: 0; border-color: #000000; border-width: 0.25mm; color: #777777; min-width: 5cm; min-height: 2.8125cm; } grid#pager { outline-color: #000000; outline-style: dashed; outline-width: 0.25mm; } grid#switcher_active image, grid#switcher_active { min-width: 50px; min-height: 50px; padding: 5px; background-color: #646464; border-radius: 5px; -GtkWidget-hexpand: true; } grid#switcher_normal image, grid#switcher_normal { min-width: 50px; min-height: 50px; padding: 5px; -GtkWidget-direction: right; -GtkWidget-hexpand: true; } window#switcher { border: 1px solid @borders; border-radius: 5px; padding: 5px; -GtkWidget-hexpand: true; } grid#switcher { border-radius: 5px; padding: 5px; -GtkWidget-hexpand: true; } button#tray_active, button#tray_passive, button#tray_attention { border: 0px; margin: 0px; border-radius: 0px; } button#tray_active label, button#tray_passive label, button#tray_attention label { padding: 1px; color: @theme_text_color; } button#tray_active image, button#tray_passive image, button#tray_attention image { min-width: 18px; min-height: 18px; -GtkWidget-vexpand: true; -GtkWidget-halign: center; -GtkWidget-valign: center; } label#clock { color: @theme_text_color; -GtkWidget-vexpand: true; -GtkWidget-valign: center; font-size: 12px; } label#sway_lang { margin-left: 3px; margin-right: 5px; } grid#layout { -GtkWidget-direction: right; } button#module, button#showdesktop, button#startmenu { margin: 0px; border: 0px; border-radius: 0px; min-height: 18px; min-width: 18px; -GtkWidget-valign: center; -GtkWidget-vexpand: true; padding: 0px 3px 0px 3px; } button#startmenu, button#showdesktop { min-width: 24px; min-height: 24px; } * { color: #ffffff; } sfwbar-1.0~beta13/config/wbar-backlight.widget000066400000000000000000000006701450657570000214030ustar00rootroot00000000000000scanner { file("/sys/class/backlight/*/actual_brightness") { BacklightActual = Grab(Sum) } file("/sys/class/backlight/*/max_brightness") { BacklightMax = Grab(Sum) } } module("bsdctl") Set XBacklight = If(!Ident(BSDCtl), BacklightActual/BacklightMax*100, Val(BSDCtl("hw.acpi.video.lcd0.brightness")) ) layout { label { style = "backlight" interval = 2000 value = $XBacklight + '% ' } } sfwbar-1.0~beta13/config/wbar-battery.widget000066400000000000000000000017041450657570000211240ustar00rootroot00000000000000scanner { file("/sys/class/power_supply/BAT0/charge_full") { BatteryTotal = Grab(Sum) } file("/sys/class/power_supply/BAT0/charge_now") { BatteryChargeNow = Grab(Sum) } file("/sys/class/power_supply/BAT0/energy_now") { BatteryEnergyNow = Grab(Sum) } file("/sys/class/power_supply/BAT0/status") { BatteryStatus = RegEx("^(.*)$") } } module("bsdctl") set BatteryLeft = If(BatteryChargeNow.count,BatteryChargeNow,BatteryEnergyNow) set XBatteryLevel = If(!Ident(BSDCtl), BatteryLeft / BatteryTotal * 100, BSDCtl("hw.acpi.battery.level.life") ) set XBatteryDischarging = If(!Ident(BSDCtl), $BatteryStatus="Discharging", BSDCtl("hw.acpi.battery.state")="1" ) layout { label { style = "battery" interval = 60000 value = Str(XBatteryLevel) + '% ' + if(XBatteryDischarging, Lookup(XBatteryLevel,80,"",60,"",40,"",20,"",""), "") } } sfwbar-1.0~beta13/config/wbar-bluetooth.widget000066400000000000000000000003741450657570000214610ustar00rootroot00000000000000include("rfkill.source") Set RfKillBTVisible = Cached(RfKillBTVisible) | RfKillBluetoothSoft.count layout { label { style = If(RfKillBTVisible,"bluetooth","hidden") trigger = "rfkill" value = If(RfKillBluetoothSoft = 0," ","") } } sfwbar-1.0~beta13/config/wbar-cpu.widget000066400000000000000000000002371450657570000202410ustar00rootroot00000000000000include("cpu.source") layout { label { style = if(XCpuPresent,"cpu","hidden") interval = 10000 value = Str(XCpuUtilization*100) + '% ' } } sfwbar-1.0~beta13/config/wbar-idleinhibit.widget000066400000000000000000000006561450657570000217430ustar00rootroot00000000000000Module("idleinhibit"); layout { label { style = If(!Ident(IdleInhibitState),"hidden", If(IdleInhibitState()="on", "idle_inhibitor_activated", "idle_inhibitor")) value = If(IdleInhibitState()="on", "", "") tooltip = If(IdleInhibitState()="on", "activated", "deactivated") trigger = "idleinhibitor" action[1] = SetIdleInhibitor "toggle" } } sfwbar-1.0~beta13/config/wbar-memory.widget000066400000000000000000000002451450657570000207610ustar00rootroot00000000000000include("memory.source") layout { label { style = if(XMemPresent,"memory","hidden") interval = 30000 value = Str(XMemUtilization*100) + '% ' } } sfwbar-1.0~beta13/config/wbar-mpd.widget000066400000000000000000000022231450657570000202270ustar00rootroot00000000000000include("mpd.source") Set XMpdElapsed = MpdElapsed+If($MpdState="play",MpdElapsed.age/1000000,0) Set XMpdRandom = If(!MpdRandom,'','') function("SetMpd") { SetValue "Mpd", If($MpdState="disconnected", "Disconnected", Map($MpdState,"pause"," ","play","","") + $XMpdRandom + " "+ '' + If(!MpdRepeat,""," ")+ if($MpdState="stop","Stopped", $MpdArtist + " - " + $MpdAlbum + " - " + $MpdTitle + " (" + Pad(Str((XMpdElapsed-XMpdElapsed%60)/60),2,"0") + ":" + Pad(Str(XMpdElapsed%60),2,"0") + "/" + Pad(Str((MpdDuration-MpdDuration%60)/60),2,"0") + ":" + Pad(Str(MpdDuration%60),2,"0") + ") " + " ⸨" + $MpdQPos + "|" + $MpdQLen + "⸩ " + Str(MpdVolume) + "%" ) ) + " " } TriggerAction "mpd", Function "SetMpd" layout { label "Mpd" { interval = 1000 style = Map($MpdState, "play", "mpd_play", "pause", "mpd_paused", "stop", "mpd_stopped", "mpd_disconnected" ) action[0] = Function "SetMpd" action = MpdCmd "pause" } } sfwbar-1.0~beta13/config/wbar-net.widget000066400000000000000000000014441450657570000202410ustar00rootroot00000000000000Module("network") function("ToggleNet") { [UserState] UserState "off" [UserState] SetValue If(NetInfo("ip")="", "Disconnected ⚠", NetInfo("interface") + ": " + NetInfo("ip") + "/" + NetInfo("cidr") ) [!UserState] UserState "on" [!UserState] SetValue If(NetInfo("ip")="", "Disconnected ⚠", If(NetInfo("Essid")="", NetInfo("ip") + ": " + NetInfo("cidr") + " ", NetInfo("Essid") + " (" + Str(NetStat("signal")) + "%) " ) ) } layout { label { style = If(Ident(NetInfo), If(NetInfo("ip")="", "network_disconnected", "network" ), "hidden" ) trigger = "network" action = Function "ToggleNet" action[0] = Function "ToggleNet" } } sfwbar-1.0~beta13/config/wbar-pulse.widget000066400000000000000000000012751450657570000206050ustar00rootroot00000000000000module("pulsectl") define Volume = Val(Pulse("sink-volume")) Set PulseIcon = Map(Pulse("sink-form"), "headphone", "", "hands-free", "", "headset", "", "phone", "", "portable", "", "car", "", Lookup(Volume,66,"",33,"","")) layout { label { style = If(Ident(Pulse),If(Val(Pulse("sink-mute")),"pulseaudio_muted","pulseaudio"),"hidden") trigger = "pulse" value = If(Val(Pulse("sink-mute")),"", Str(Volume) + "% ") + $PulseIcon + " " + Str(Val(Pulse("source-volume"))) + "% " action[4] = PulseCmd "sink-volume +1" action[5] = PulseCmd "sink-volume -1" } } sfwbar-1.0~beta13/config/wbar-sway-lang.widget000066400000000000000000000012021450657570000213450ustar00rootroot00000000000000module("xkbmap") scanner { SwayClient("") { SwayXkbLayout = Json(".input.input.xkb_active_layout_name") } } Set XSwayLangCommand = If(Ident($SwayLangCommand),$SwayLangCommand, "input type:keyboard xkb_switch_layout next") Function("XSwayLangInit") { SwayCmd "input type:keyboard xkb_switch_layout next" SwayCmd "input type:keyboard xkb_switch_layout prev" } layout { label { action[0] = Function "XSwayLangInit" action[1] = SwayCmd $XSwayLangCommand style = If(Ident(SwayXkbLayout) & $SwayXkbLayout!="","language","hidden") value = XkbMap($SwayXkbLayout,"description","name") trigger = "sway" } } sfwbar-1.0~beta13/config/wbar-temp.widget000066400000000000000000000003251450657570000204150ustar00rootroot00000000000000include("cpu-temp.source") layout { label { style = if(XCpuTempPresent,"temperature","hidden") interval = 10000 value = Str(XCPUTemp/1000)+'°C '+Lookup(XCpuTemp/1000,40,"",20,"","") } } sfwbar-1.0~beta13/config/wbar.config000066400000000000000000000141731450657570000174420ustar00rootroot00000000000000scanner { SwayClient("") {} # launch sway client to get "sway" trigger } # Window Placer placer { xorigin = 5 # place the first window at X% from the left yorigin = 5 # place the first window at X% from the top xstep = 5 # step by X% of desktop horizontally ystep = 5 # step by X% of desktop vertically children = true } # Task Switcher switcher { interval = 700 icons = true labels = false cols = 5 } include("winops.widget") #function("SfwbarInit") { # SetLayer "bottom" #} # Panel layout layout ":start" { pager { style = "pager" rows = 1 preview = true numeric = true } } layout ":center" { label { trigger = "sway" value = ActiveWin() css = "*{ -GtkWidget-ellipsize: true; -GtkWidget-hexpand: true; color: #ffffff; }" } } layout ":end" { include("wbar-mpd.widget") # include("wbar-bluetooth.widget") include("wbar-idleinhibit.widget") include("wbar-pulse.widget") include("wbar-net.widget") include("wbar-cpu.widget") include("wbar-memory.widget") include("wbar-temp.widget") include("wbar-backlight.widget") include("wbar-sway-lang.widget") include("wbar-battery.widget") label { style = "clock" value = Time("%H:%M") } tray { rows = 1 } } #CSS * { /* `otf-font-awesome` is required to be installed for icons */ font-family: Roboto, Helvetica, Arial, sans-serif; font-size: 13px; } window#sfwbar { -GtkWidget-direction: top; background-color: rgba(43, 48, 59, 0.5); border-bottom: 3px solid rgba(100, 114, 125, 0.5); color: #ffffff; transition-property: background-color; transition-duration: .5s; } eventbox { background-image: none; background-color: transparent; } button#pager_normal *, button#pager_visible *, button#pager_focused * { padding: 0px; color: #ffffff; } button#pager_normal, button#pager_visible, button#pager_focused { padding: 0 5px; background-color: transparent; background-image: none; color: #ffffff; /* Use box-shadow instead of border so the text isn't offset */ box-shadow: inset 0 -3px transparent; /* Avoid rounded borders under each workspace name */ border: none; border-radius: 0; min-height: 30px; } button#pager_focused { background-color: #64727D; box-shadow: inset 0 -3px #ffffff; } #mode { background-color: #64727D; border-bottom: 3px solid #ffffff; } #clock, #battery, #cpu, #memory, #disk, #temperature, #backlight, #network, #network_disconnected, #pulseaudio, #pulseaudio_muted, #custom-media, #tray, #mode, #idle_inhibitor, #idle_inhibitor_activated, #bluetooth, #mpd { padding: 0 10px; color: #ffffff; margin-right: 4px; } #window, #workspaces { margin: 0 4px; } #clock { background-color: #64727D; } #bluetooth { background-color: #84225D; } #battery { background-color: #ffffff; color: #000000; } #battery_charging, #battery_plugged { color: #ffffff; background-color: #26A65B; } @keyframes blink { to { background-color: #ffffff; color: #000000; } } #battery.critical:not(.charging) { background-color: #f53c3c; color: #ffffff; animation-name: blink; animation-duration: 0.5s; animation-timing-function: linear; animation-iteration-count: infinite; animation-direction: alternate; } label { color: #000000; -GtkWidget-vexpand: true; -GtkWidget-ellipsize: false; } label:focus { background-color: #000000; } #cpu { background-color: #2ecc71; color: #000000; } #memory { background-color: #9b59b6; } #disk { background-color: #964B00; } #backlight { background-color: #90b1b1; } #network { background-color: #2980b9; } #network_disconnected { background-color: #f53c3c; } #pulseaudio { background-color: #f1c40f; color: #000000; } #pulseaudio_muted { background-color: #90b1b1; color: #2a5c45; } #custom-media { background-color: #66cc99; color: #2a5c45; min-width: 100px; } #custom-media.custom-spotify { background-color: #66cc99; } #custom-media.custom-vlc { background-color: #ffa000; } #temperature { background-color: #f0932b; } #temperature.critical { background-color: #eb4d4b; } image#tray_active, image#tray_passive, image#tray_attention { background-color: #2980b9; padding: 7px 5px; } image#tray_passive { -gtk-icon-effect: dim; } image#tray_attention { -gtk-icon-effect: highlight; background-color: #eb4d4b; } #idle_inhibitor { background-color: #2d3436; } #idle_inhibitor_activated { background-color: #ecf0f1; color: #2d3436; } #mpd_play, #mpd_paused, #mpd_stopped, #mpd_disconnected, #mpd { background-color: #66cc99; color: #2a5c45; padding: 0 10px; margin-right: 4px; } #mpd_disconnected { -GtkWidget-visible: false; background-color: #f53c3c; } #mpd_stopped { background-color: #90b1b1; } #mpd_paused { background-color: #51a37a; } #language { background: #00b093; color: #740864; padding: 0 5px; margin: 0 5px; margin-right: 9px; min-width: 16px; } #keyboard-state { background: #97e1ad; color: #000000; padding: 0 0px; margin: 0 5px; min-width: 16px; } #keyboard-state > label { padding: 0 5px; } #keyboard-state > label.locked { background: rgba(0, 0, 0, 0.2); } #clock { background-color: #64727D; } #hidden { -GtkWidget-visible: false; } button#pager_preview { background-image: none; border-radius: 0; border-image: none; border-color: #000000; border-width: 0.25mm; color: #777777; min-width: 5cm; min-height: 2.8125cm; } grid#switcher_active *, grid#switcher_active, grid#switcher_active * * { min-width: 1.25cm; min-height: 1.25cm; border-image: none; padding: 1.25mm; background-color: #2980b9; border-radius: 1.25mm; } grid#switcher_normal *, grid#switcher_normal, grid#switcher_normal * * { min-width: 1.25cm; min-height: 1.25cm; border-image: none; padding: 1.25mm; } window#switcher { border-style: solid; border-width: 0.25mm; border-color: #000000; border-radius: 1.25mm; padding: 1.25mm; -GtkWidget-hexpand: true; background-color: rgba(43, 48, 59, 0.5); border: 3px solid rgba(100, 114, 125, 0.5); color: #ffffff; transition-property: background-color; transition-duration: .5s; } grid#switcher { border-radius: 1.25mm; padding: 1.25mm; } sfwbar-1.0~beta13/config/weather.widget000066400000000000000000000013331450657570000201560ustar00rootroot00000000000000# Parse met.no weather forecast scanner { Exec("wget -O - \"https://api.met.no/weatherapi/locationforecast/2.0/compact?lat=51.8711&lon=0.1587\"") { WthrTemp= Json(".properties.timeseries.1.data.instant.details.air_temperature") WthrCond= Json(".properties.timeseries.1.data.next_12_hours.summary.symbol_code") } } layout { style = "frame" label { interval = 300000 css = "* { font: 0.5cm Sans; font-weight: 700; -GtkWidget-align: 0.5; padding-left: 1mm; padding-right: 1mm; }" value = Str(WthrTemp.val,1) } image { interval = 300000 css = "* { -GtkWidget-hexpand: true; -GtkWidget-vexpand: true; padding-left: 1mm; padding-right: 1mm; }" value = 'icons/weather/'+$WthrCond+'.svg' } } sfwbar-1.0~beta13/config/winops.widget000066400000000000000000000005171450657570000200410ustar00rootroot00000000000000function("ToggleMinimize") { [!Minimized] Minimize [Minimized] UnMinimize } function("ToggleMaximize") { [!Maximized] Maximize [Maximized] UnMaximize } menu("winops") { item("focus", Focus ); item("close", Close ); item("(un)minimize", Function "ToggleMinimize" ); item("(un)maximize", Function "ToggleMaximize" ); } sfwbar-1.0~beta13/doc/000077500000000000000000000000001450657570000146125ustar00rootroot00000000000000sfwbar-1.0~beta13/doc/ChangeLog000066400000000000000000000067271450657570000164000ustar00rootroot000000000000001.0beta13 - Implement bluez module - Add WidgetId() expression function, assign id's to all widgets - Add Eval action - Add SwitcherEvent action. Support backwards switching. - Allow config to modify widgets. Implement ClearWidget action. 1.0beta12 - Implement mirroring of taskbar across multiple monitors - Show on hover taskbar support - Add ALSA module - Emit triggers on RT signals - Support modifier keys in action triggers - Allow closing popup windows on focus out 1.0_beta11 - Add BSDCtl module to query BSD sysctl information - Stability improvements for client scanner sources - Update internal mpd client to new client source api - Implement bar reload on SIGHUP 1.0_beta10 - Implement loadable modules - Add support for scriptable popup windows - Implement intermediate variables - Support flipping grid sort order - Improve taskbar filtering - Support symbolic icons - Support launching via swaybar_command 1.0_beta9 - Implement grouped taskbars - Add support for Hyprland IPC - Implement filtering by workspace (on supported IPCs) - Improve support for dynamically generated menus - Implement second user state 1.0_beta8 - Update config files 1.0_beta7 - Implement chart widget - Support loading svg images from inline data - Implement centering of widgets in respect to the bar - Support multiple bars, taskbars and pagers - Implement addressable bars / widgets - Allow hiding widgets via css property 1.0_beta6 - Improved json path parser - Support for listening to all sway events - Support for show/hide on modifier - Support for continuous listening to output of an executed program or a socket - Support for dynamic recoloring of images - Support for user state of widgets - Support for configuration of layer shell exclusive zone - Fixes for icon matching - Fixes for SNI tray support 1.0_beta4 - Implement tooltips - Implement dynamci styling - MPD support - Idle Inhibit protocol support - Taskbar output filter (only show windows from a given output) - SetBarSize action - title_width property to trim long window titles - SetBarID action (listen to events on a specific bar ID) - SetValue / SetStyle actions (control widget visuals via actions) 1.0_beta3 - Implement user defined menues, functions and actions - Allow binding actions to multiple mouse events for any widget 1.0_beta2 - Implement tray icon menues - Lookup XDG data system directories for resources - Update default configs to be more useful 1.0_beta1 - Implement system tray - Move configuration from libucl to custom parser - Move expression parser to custom parser - Move data extraction and expression engine to a separate thread 0.9.10 - Add support for wayland foreign toplevel protocol (taskbar support for wayfire and other compositors supporting this protocol) - Implement json parser for scan variables - Add scalabale image widget to have more consistent CSS theming - Bug fixes 0.9.9.3 - Bug fixes 0.9.9.2 - Bug fixes 0.9.9.1 - Bug fixes 0.9.9 - Make configuration modular - Implement Df command to check disc free space - Allow placing taskbar on specified edge - Allow specifying timezone for Time command 0.9.8 - Implement pager preview - Add icons 0.9.7 - Implement task switcher - Improve placement logic 0.9.6 - Migrate config to libucl - Implement css theming 0.9.4 - Allow specifying config file through command line 0.9.3 - Implement pager sfwbar-1.0~beta13/doc/sfwbar-alsa.1000066400000000000000000000051131450657570000170760ustar00rootroot00000000000000.\" Man page generated from reStructuredText. . . .nr rst2man-indent-level 0 . .de1 rstReportMargin \\$1 \\n[an-margin] level \\n[rst2man-indent-level] level margin: \\n[rst2man-indent\\n[rst2man-indent-level]] - \\n[rst2man-indent0] \\n[rst2man-indent1] \\n[rst2man-indent2] .. .de1 INDENT .\" .rstReportMargin pre: . RS \\$1 . nr rst2man-indent\\n[rst2man-indent-level] \\n[an-margin] . nr rst2man-indent-level +1 .\" .rstReportMargin post: .. .de UNINDENT . RE .\" indent \\n[an-margin] .\" old: \\n[rst2man-indent\\n[rst2man-indent-level]] .nr rst2man-indent-level -1 .\" new: \\n[rst2man-indent\\n[rst2man-indent-level]] .in \\n[rst2man-indent\\n[rst2man-indent-level]]u .. .TH "SFWBAR-ALSA" 1 "" "" .SH NAME sfwbar-alsa \- Sfwbar ALSA module .sp Filename: alsactl.so .sp Requires: libasound .SH SYNOPSIS .sp The ALSA module provides an interface to query and adjust the settings of the ALSA sound system. .SH EXPRESSION FUNCTIONS .SS Alsa(Query[,Element]) .sp function Alsa queries the state of the ALSA system, the Query parameter specifies the information to query, the optional Element parameter specifies the mixer element to which the query applies. If the Element isn\(aqt given the query is applied to the \(dqMaster\(dq element or the current card. The supported query types are: .INDENT 0.0 .TP .B \(dqplayback\-volume\(dq the volume of a sink. .TP .B \(dqplayback\-mute\(dq muted state of the sink. .TP .B \(dqcapture\-volume\(dq the volume of a source. .TP .B \(dqcapture\-mute\(dq muted state of the source. .UNINDENT .SH ACTIONS .SS AlsaCmd [Interface,],Command .sp Manipulate the state of the interface, if the interface isn\(aqt specified, the Command will be applied to the current interface. .INDENT 0.0 .TP .B \(dqplayback\-volume +/\-X\(dq Adjust the playback volume by X%. If the value isn\(aqt prefixed by either \fI+\fP or \fI\-\fP, the volume is set to the percentage value X. .TP .B \(dqplayback\-mute State\(dq Change the playback mute state, State can be On, Off or Toggle. .TP .B \(dqcapture\-volume +/\-X\(dq Adjust the capture volume by X%. If the value isn\(aqt prefixed by either \fI+\fP or \fI\-\fP, the volume is set to the percentage value X. .TP .B \(dqcapture\-mute State\(dq Change the capture mute state, State can be On, Off or Toggle. .UNINDENT .SS AlsaSetCard Card .sp Select ALSA sound card. This action accepts one paramter with a card name, by default the module will use \(dqdefault\(dq card. .SH TRIGGERS .sp The module defines one trigger \(dqAlsa\(dq which is emitted whenever the state of the ALSA system changes. .SH COPYRIGHT GPLv3+ .\" Generated by docutils manpage writer. . sfwbar-1.0~beta13/doc/sfwbar-alsa.rst000066400000000000000000000034721450657570000175540ustar00rootroot00000000000000sfwbar-alsa ########### ################## Sfwbar ALSA module ################## :Copyright: GPLv3+ :Manual section: 1 Filename: alsactl.so Requires: libasound SYNOPSIS ======== The ALSA module provides an interface to query and adjust the settings of the ALSA sound system. Expression Functions ==================== Alsa(Query[,Element]) ------------------------ function Alsa queries the state of the ALSA system, the Query parameter specifies the information to query, the optional Element parameter specifies the mixer element to which the query applies. If the Element isn't given the query is applied to the "Master" element or the current card. The supported query types are: "playback-volume" the volume of a sink. "playback-mute" muted state of the sink. "capture-volume" the volume of a source. "capture-mute" muted state of the source. Actions ======= AlsaCmd [Interface,],Command ----------------------------- Manipulate the state of the interface, if the interface isn't specified, the Command will be applied to the current interface. "playback-volume +/-X" Adjust the playback volume by X%. If the value isn't prefixed by either `+` or `-`, the volume is set to the percentage value X. "playback-mute State" Change the playback mute state, State can be On, Off or Toggle. "capture-volume +/-X" Adjust the capture volume by X%. If the value isn't prefixed by either `+` or `-`, the volume is set to the percentage value X. "capture-mute State" Change the capture mute state, State can be On, Off or Toggle. AlsaSetCard Card ----------------------------- Select ALSA sound card. This action accepts one paramter with a card name, by default the module will use "default" card. Triggers ======== The module defines one trigger "Alsa" which is emitted whenever the state of the ALSA system changes. sfwbar-1.0~beta13/doc/sfwbar-bluez.1000066400000000000000000000107661450657570000173110ustar00rootroot00000000000000.\" Man page generated from reStructuredText. . . .nr rst2man-indent-level 0 . .de1 rstReportMargin \\$1 \\n[an-margin] level \\n[rst2man-indent-level] level margin: \\n[rst2man-indent\\n[rst2man-indent-level]] - \\n[rst2man-indent0] \\n[rst2man-indent1] \\n[rst2man-indent2] .. .de1 INDENT .\" .rstReportMargin pre: . RS \\$1 . nr rst2man-indent\\n[rst2man-indent-level] \\n[an-margin] . nr rst2man-indent-level +1 .\" .rstReportMargin post: .. .de UNINDENT . RE .\" indent \\n[an-margin] .\" old: \\n[rst2man-indent\\n[rst2man-indent-level]] .nr rst2man-indent-level -1 .\" new: \\n[rst2man-indent\\n[rst2man-indent-level]] .in \\n[rst2man-indent\\n[rst2man-indent-level]]u .. .TH "SFWBAR-BLUEZ" 1 "" "" .SH NAME sfwbar-bluez \- Sfwbar Bluez module .sp Filename: bluez.so .sp Requires: none .SH SYNOPSIS .sp The bluez module provides an interface to connect and disconnect bluetooth devices via the bluez server. .SH EXPRESSION FUNCTIONS .SS BluezGet(Property) .sp function BluezGet fetches a property for a currently advertized bluez device. The supported properties are: .INDENT 0.0 .TP .B \(dqPath\(dq an object path of the device, this is used as a unique id to provide notifivations of changes or destruction of the device. .TP .B \(dqRemovedPath\(dq an object path of the most recently removed device. This is populated upon emission of the \(dqbluez_removed\(dq trigger. .TP .B \(dqAddress\(dq a bluetooth address of the device .TP .B \(dqName\(dq a name of the device .TP .B \(dqIcon\(dq an icon for the device type .UNINDENT .SS BluezState(Property) .sp function BluezState fetches a boolean state property of the currently advertized bluez device. The supported properties are: .INDENT 0.0 .TP .B \(dqPaired\(dq specifies whether the device is paired to the adapter .TP .B \(dqTrusted\(dq specifies whether the device is trusted by the adapter .TP .B \(dqConnected\(dq specifies whether the device is connected to the adpater .TP .B \(dqRunning\(dq queries the global state of the bluez module. If the module is connected to the server and sees at least one bluetooth adapter, this query will return true. Otherwise it will return false. .UNINDENT .SH ACTIONS .SS BluezScan .sp Initiate scan for the bluetooth devices. The scan will last for the duration specified in milliseconds. .SS BluezAck .sp Notify the module that all information for the currently advertized device has been processed. The module may then emit another bluez_changed event if further device updates are available. .SS BluezAckRemoved .sp Notify the module that all information for the currently advertized device removal has been processed. The module may then emit another bluez_removed event if further device removals are queued. .SS BluezConnect .sp Attempt to connect to the bluetooth device specified by the path. .SS BluezPair .sp Attempt to pair and connect to the bluetooth device specified by the path. .SS BluezDisconnect .sp Attempt to diconnect from the bluetooth device specified by the path. .SS BluezRemove .sp Attempt to remove the bluetooth device specified by the path. .SH TRIGGERS .sp The module defines the following triggers: .INDENT 0.0 .TP .B \(dqbluez_running\(dq this trigger is emitted when connected to the first bluetooth adapter has been made or when connection to the last adapter is lost (i.e. when the value of BluezState(\(dqRunning\(dq) query has changed) .TP .B \(dqbluez_changed\(dq this trigger is emitted when a new bluetooth device is discovered or properties of a device are changed. Once the trigger is emitted, the information for the relevant device will be available via BluezGet and BluezState expression functions. Once the config finished handling the device update, it should call action BluezAck. Upon receipt of this action, the module may emit \(dqbluez_changed\(dq again if further device updates are queued. .TP .B \(dqbluez_removed\(dq this trigger is emitted when a bluetooth device is no longer available and should be removed from the layout. The path of the removed device is available via BluezGet(\(dqRemovedPath\(dq). Once the config finished removing the device, it should call action BluezAckRemoved. Upon receipt of this action, the module may emit another \(dqbluez_removed\(dq trigger if further devices have been removed. .TP .B \(dqbluez_scan\(dq this trigger is emitted when scan for bluetooth devices has been initiated. .TP .B \(dqbluez_scan_complete\(dq this trigger is emitted when scan for bluetooth devices is complete. .UNINDENT .SH COPYRIGHT GPLv3+ .\" Generated by docutils manpage writer. . sfwbar-1.0~beta13/doc/sfwbar-bluez.rst000066400000000000000000000074011450657570000177510ustar00rootroot00000000000000sfwbar-bluez ############ ################### Sfwbar Bluez module ################### :Copyright: GPLv3+ :Manual section: 1 Filename: bluez.so Requires: none SYNOPSIS ======== The bluez module provides an interface to connect and disconnect bluetooth devices via the bluez server. Expression Functions ==================== BluezGet(Property) ------------------ function BluezGet fetches a property for a currently advertized bluez device. The supported properties are: "Path" an object path of the device, this is used as a unique id to provide notifivations of changes or destruction of the device. "RemovedPath" an object path of the most recently removed device. This is populated upon emission of the "bluez_removed" trigger. "Address" a bluetooth address of the device "Name" a name of the device "Icon" an icon for the device type BluezState(Property) -------------------- function BluezState fetches a boolean state property of the currently advertized bluez device. The supported properties are: "Paired" specifies whether the device is paired to the adapter "Trusted" specifies whether the device is trusted by the adapter "Connected" specifies whether the device is connected to the adpater "Running" queries the global state of the bluez module. If the module is connected to the server and sees at least one bluetooth adapter, this query will return true. Otherwise it will return false. Actions ======= BluezScan -------------------- Initiate scan for the bluetooth devices. The scan will last for the duration specified in milliseconds. BluezAck -------- Notify the module that all information for the currently advertized device has been processed. The module may then emit another bluez_changed event if further device updates are available. BluezAckRemoved --------------- Notify the module that all information for the currently advertized device removal has been processed. The module may then emit another bluez_removed event if further device removals are queued. BluezConnect ------------------- Attempt to connect to the bluetooth device specified by the path. BluezPair ---------------- Attempt to pair and connect to the bluetooth device specified by the path. BluezDisconnect ---------------------- Attempt to diconnect from the bluetooth device specified by the path. BluezRemove ------------------ Attempt to remove the bluetooth device specified by the path. Triggers ======== The module defines the following triggers: "bluez_running" this trigger is emitted when connected to the first bluetooth adapter has been made or when connection to the last adapter is lost (i.e. when the value of BluezState("Running") query has changed) "bluez_changed" this trigger is emitted when a new bluetooth device is discovered or properties of a device are changed. Once the trigger is emitted, the information for the relevant device will be available via BluezGet and BluezState expression functions. Once the config finished handling the device update, it should call action BluezAck. Upon receipt of this action, the module may emit "bluez_changed" again if further device updates are queued. "bluez_removed" this trigger is emitted when a bluetooth device is no longer available and should be removed from the layout. The path of the removed device is available via BluezGet("RemovedPath"). Once the config finished removing the device, it should call action BluezAckRemoved. Upon receipt of this action, the module may emit another "bluez_removed" trigger if further devices have been removed. "bluez_scan" this trigger is emitted when scan for bluetooth devices has been initiated. "bluez_scan_complete" this trigger is emitted when scan for bluetooth devices is complete. sfwbar-1.0~beta13/doc/sfwbar-bsdctl.1000066400000000000000000000023241450657570000174320ustar00rootroot00000000000000.\" Man page generated from reStructuredText. . . .nr rst2man-indent-level 0 . .de1 rstReportMargin \\$1 \\n[an-margin] level \\n[rst2man-indent-level] level margin: \\n[rst2man-indent\\n[rst2man-indent-level]] - \\n[rst2man-indent0] \\n[rst2man-indent1] \\n[rst2man-indent2] .. .de1 INDENT .\" .rstReportMargin pre: . RS \\$1 . nr rst2man-indent\\n[rst2man-indent-level] \\n[an-margin] . nr rst2man-indent-level +1 .\" .rstReportMargin post: .. .de UNINDENT . RE .\" indent \\n[an-margin] .\" old: \\n[rst2man-indent\\n[rst2man-indent-level]] .nr rst2man-indent-level -1 .\" new: \\n[rst2man-indent\\n[rst2man-indent-level]] .in \\n[rst2man-indent\\n[rst2man-indent-level]]u .. .TH "SFWBAR-BSDCTL" 1 "" "" "" .SH NAME sfwbar-bsdctl \- Sfwbar BSD sysctl module .sp Filename: bsdctl.so .sp Requires: BSD libc .SH SYNOPSIS .sp The BSDCtl module provides an interface to query BSD sysctl values .SH EXPRESSION FUNCTIONS .SS BSDCtl(Query) .sp function BSDCtl queries the value of a sysctl variable. It\(aqs equivalent to calling sysctl program on BSD system. Please note that you can only query individual nodes rather than groups. .SH ACTIONS .sp None .SH TRIGGERS .sp None .SH COPYRIGHT GPLv3+ .\" Generated by docutils manpage writer. . sfwbar-1.0~beta13/doc/sfwbar-bsdctl.rst000066400000000000000000000011021450657570000200730ustar00rootroot00000000000000sfwbar-bsdctl ############# ######################## Sfwbar BSD sysctl module ######################## :Copyright: GPLv3+ :Manual section: 1 Filename: bsdctl.so Requires: BSD libc SYNOPSIS ======== The BSDCtl module provides an interface to query BSD sysctl values Expression Functions ==================== BSDCtl(Query) ------------- function BSDCtl queries the value of a sysctl variable. It's equivalent to calling sysctl program on BSD system. Please note that you can only query individual nodes rather than groups. Actions ======= None Triggers ======== None sfwbar-1.0~beta13/doc/sfwbar-idleinhibit.1000066400000000000000000000036521450657570000204500ustar00rootroot00000000000000.\" Man page generated from reStructuredText. . . .nr rst2man-indent-level 0 . .de1 rstReportMargin \\$1 \\n[an-margin] level \\n[rst2man-indent-level] level margin: \\n[rst2man-indent\\n[rst2man-indent-level]] - \\n[rst2man-indent0] \\n[rst2man-indent1] \\n[rst2man-indent2] .. .de1 INDENT .\" .rstReportMargin pre: . RS \\$1 . nr rst2man-indent\\n[rst2man-indent-level] \\n[an-margin] . nr rst2man-indent-level +1 .\" .rstReportMargin post: .. .de UNINDENT . RE .\" indent \\n[an-margin] .\" old: \\n[rst2man-indent\\n[rst2man-indent-level]] .nr rst2man-indent-level -1 .\" new: \\n[rst2man-indent\\n[rst2man-indent-level]] .in \\n[rst2man-indent\\n[rst2man-indent-level]]u .. .TH "SFWBAR-IDLEINHIBIT" 1 "" "" .SH NAME sfwbar-idleinhibit \- Sfwbar IdleInhibitor module .sp Filename: idleinhibit.so .sp Requires: none .SH SYNOPSIS .sp The IdleInhibit module, allows attaching idle inhibitor to any widget in the taskbar. If an idle inhibitor is active on a visible widget, it will prevent any the compiositor to going into an idle state (i.e. blanking a screen, going into a suspend mode or activating a screensaver) .SH EXPRESSION FUNCTIONS .SS IdleInhibitState() .sp Query an idle inhibitor state on a calling widget. It return a string with possible values of \(dqOn\(dq, if an idle inhibitor is set on the widget or \(dqOff\(dq if it isn\(aqt. If the function is called from an expression which isn\(aqt attached to a widget, the returned value will be \(dqOff\(dq. .SH ACTIONS .SS SetIdleInhibitor Command .sp Set idle inhibitor state for a widget. The possible command values are: .INDENT 0.0 .TP .B \(dqOn\(dq turn on an idle inhibitor .TP .B \(dqOff\(dq turn off an idle inhibitor .TP .B \(dqToggle\(dq toggle the state of an idle inhibitor .UNINDENT .SH TRIGGERS .sp The module defines one trigger \(dqIdleInhibitor\(dq which is emitted whenever the state of any idle inhibitor changes. .SH COPYRIGHT GPLv3+ .\" Generated by docutils manpage writer. . sfwbar-1.0~beta13/doc/sfwbar-idleinhibit.rst000066400000000000000000000023701450657570000211140ustar00rootroot00000000000000sfwbar-idleinhibit ################## ########################### Sfwbar IdleInhibitor module ########################### :Copyright: GPLv3+ :Manual section: 1 Filename: idleinhibit.so Requires: none SYNOPSIS ======== The IdleInhibit module, allows attaching idle inhibitor to any widget in the taskbar. If an idle inhibitor is active on a visible widget, it will prevent any the compiositor to going into an idle state (i.e. blanking a screen, going into a suspend mode or activating a screensaver) Expression Functions ==================== IdleInhibitState() ------------------------ Query an idle inhibitor state on a calling widget. It return a string with possible values of "On", if an idle inhibitor is set on the widget or "Off" if it isn't. If the function is called from an expression which isn't attached to a widget, the returned value will be "Off". Actions ======= SetIdleInhibitor Command ----------------------------- Set idle inhibitor state for a widget. The possible command values are: "On" turn on an idle inhibitor "Off" turn off an idle inhibitor "Toggle" toggle the state of an idle inhibitor Triggers ======== The module defines one trigger "IdleInhibitor" which is emitted whenever the state of any idle inhibitor changes. sfwbar-1.0~beta13/doc/sfwbar-mpd.1000066400000000000000000000052371450657570000167450ustar00rootroot00000000000000.\" Man page generated from reStructuredText. . . .nr rst2man-indent-level 0 . .de1 rstReportMargin \\$1 \\n[an-margin] level \\n[rst2man-indent-level] level margin: \\n[rst2man-indent\\n[rst2man-indent-level]] - \\n[rst2man-indent0] \\n[rst2man-indent1] \\n[rst2man-indent2] .. .de1 INDENT .\" .rstReportMargin pre: . RS \\$1 . nr rst2man-indent\\n[rst2man-indent-level] \\n[an-margin] . nr rst2man-indent-level +1 .\" .rstReportMargin post: .. .de UNINDENT . RE .\" indent \\n[an-margin] .\" old: \\n[rst2man-indent\\n[rst2man-indent-level]] .nr rst2man-indent-level -1 .\" new: \\n[rst2man-indent\\n[rst2man-indent-level]] .in \\n[rst2man-indent\\n[rst2man-indent-level]]u .. .TH "SFWBAR-MPD" 1 "" "" "" .SH NAME sfwbar-mpd \- Sfwbar Music Player Daemon module .sp Filename: mpd.so .sp Requires: libmpdclient .SH SYNOPSIS .sp The Music Player Daemon module provides an interface to control an MPD server. .SH EXPRESSION FUNCTIONS .SS Mpd(Query) .sp Function Mpd queries the state of the MPD server. The function returns a string. the following queries are supported: .INDENT 0.0 .TP .B \(dqtitle\(dq The title of current song. .TP .B \(dqtrack\(dq Track number of the current song. .TP .B \(dqartist\(dq Artist of the current song. .TP .B \(dqalbum\(dq Album of the current song. .TP .B \(dqgenre\(dq Genre of the current song. .TP .B \(dqvolume\(dq Current playback volume. .TP .B \(dqrepeat\(dq Repet flag of the current playlist: \(dq1\(dq/\(dq0\(dq. .TP .B \(dqrandom\(dq Random flag of the current playlist: \(dq1\(dq/\(dq0\(dq. .TP .B \(dqqueue_len\(dq Length of the current playlist. .TP .B \(dqqueue_pos\(dq Number of the current song in the playlist. .TP .B \(dqelapsed\(dq Elapsed time since the beginning of the song. .TP .B \(dqlength\(dq Length of the current song. .TP .B \(dqrate\(dq Sample rate of the current song (in kbps). .TP .B \(dqstate\(dq State of the player: \(dqplay\(dq,\(dqpause\(dq,\(dqstop\(dq,\(dqunknown\(dq. .UNINDENT .SH ACTIONS .SS MpdSetPassword \(dqPassword\(dq .sp Specify a password used to connect to the MPD server. .SS MpdCommand \(dqCommand\(dq .sp Send a command to the MPD server. The supported commands are: .INDENT 0.0 .TP .B \(dqplay\(dq Play the current song. .TP .B \(dqpause\(dq Pause playback. .TP .B \(dqstop\(dq Stop playback. .TP .B \(dqprev\(dq Switch to the previous song in the playlist. .TP .B \(dqnext\(dq Switch to the next song in the playlist. .UNINDENT .SH TRIGGERS .sp The module defines two triggers: .INDENT 0.0 .TP .B \(dqmpd\(dq Emitted whenever the state of the MPD server changes. .TP .B \(dqmpd\-progress\(dq Emitted every second while player is in \(dqplay\(dq state. .UNINDENT .SH COPYRIGHT GPLv3+ .\" Generated by docutils manpage writer. . sfwbar-1.0~beta13/doc/sfwbar-mpd.rst000066400000000000000000000033541450657570000174130ustar00rootroot00000000000000sfwbar-mpd ############ ################################# Sfwbar Music Player Daemon module ################################# :Copyright: GPLv3+ :Manual section: 1 Filename: mpd.so Requires: libmpdclient SYNOPSIS ======== The Music Player Daemon module provides an interface to control an MPD server. Expression Functions ==================== Mpd(Query) ------------------------ Function Mpd queries the state of the MPD server. The function returns a string. the following queries are supported: "title" The title of current song. "track" Track number of the current song. "artist" Artist of the current song. "album" Album of the current song. "genre" Genre of the current song. "volume" Current playback volume. "repeat" Repet flag of the current playlist: "1"/"0". "random" Random flag of the current playlist: "1"/"0". "queue_len" Length of the current playlist. "queue_pos" Number of the current song in the playlist. "elapsed" Elapsed time since the beginning of the song. "length" Length of the current song. "rate" Sample rate of the current song (in kbps). "state" State of the player: "play","pause","stop","unknown". Actions ======= MpdSetPassword "Password" ------------------------- Specify a password used to connect to the MPD server. MpdCommand "Command" -------------------- Send a command to the MPD server. The supported commands are: "play" Play the current song. "pause" Pause playback. "stop" Stop playback. "prev" Switch to the previous song in the playlist. "next" Switch to the next song in the playlist. Triggers ======== The module defines two triggers: "mpd" Emitted whenever the state of the MPD server changes. "mpd-progress" Emitted every second while player is in "play" state. sfwbar-1.0~beta13/doc/sfwbar-network.1000066400000000000000000000046251450657570000176560ustar00rootroot00000000000000.\" Man page generated from reStructuredText. . . .nr rst2man-indent-level 0 . .de1 rstReportMargin \\$1 \\n[an-margin] level \\n[rst2man-indent-level] level margin: \\n[rst2man-indent\\n[rst2man-indent-level]] - \\n[rst2man-indent0] \\n[rst2man-indent1] \\n[rst2man-indent2] .. .de1 INDENT .\" .rstReportMargin pre: . RS \\$1 . nr rst2man-indent\\n[rst2man-indent-level] \\n[an-margin] . nr rst2man-indent-level +1 .\" .rstReportMargin post: .. .de UNINDENT . RE .\" indent \\n[an-margin] .\" old: \\n[rst2man-indent\\n[rst2man-indent-level]] .nr rst2man-indent-level -1 .\" new: \\n[rst2man-indent\\n[rst2man-indent-level]] .in \\n[rst2man-indent\\n[rst2man-indent-level]]u .. .TH "SFWBAR-NETWORK" 1 "" "" "" .SH NAME sfwbar-network \- Sfwbar Network module .sp Filename: network.so .sp Requires: none .SH SYNOPSIS .sp The network module tracks te state of the current network connection. .SH EXPRESSION FUNCTIONS .SS NetInfo(Query[,Interface]) .sp Function NetInfo queries the parameter of the connection on the network interface specified. If Interface is not specifies, NetInfo will query the interface of the defult gateway (if one exists). The queries supported are: .INDENT 0.0 .TP .B \(dqip\(dq IP address of the interface. .TP .B \(dqmask\(dq Net mask of the interface. .TP .B \(dqcidr\(dq Net mask in the CIDR notation. .TP .B \(dqgateway\(dq The default gateway (not necessarily associated with the interface). .TP .B \(dqip6\(dq IPv6 IP address of the interface. .TP .B \(dqmask6\(dq IPv6 netmask of the interfce. .TP .B \(dqgateway6\(dq The default IPv6 gateway. .TP .B \(dqessid\(dq ESSID of the wireless connection (if applicable for the interface). .UNINDENT .sp NetInfo returns a string value. .SS NetStat(Query[,Interface]) .sp Function NetStat queries statistics of the interface. If the interface isn\(aqt specified, it will be applied to the interface of the default gateway. The queries supported are: .INDENT 0.0 .TP .B \(dqrxrate\(dq Receive data rate on the interface (in bps). .TP .B \(dqtxrate\(dq Transmit data rate on the interface (in bps). .TP .B \(dqsignal\(dq Signal strength of the wifi connection (if applicable). .UNINDENT .sp NetState returns a numeric value. .SH ACTIONS .sp None .SH TRIGGERS .sp The module defines one trigger \(dqnetwork\(dq which is emitted whenever the interface data is changes (i.e. ip, netmask, default gateway, wifi essid). .SH COPYRIGHT GPLv3+ .\" Generated by docutils manpage writer. . sfwbar-1.0~beta13/doc/sfwbar-network.rst000066400000000000000000000031601450657570000203170ustar00rootroot00000000000000sfwbar-network ############## ##################### Sfwbar Network module ##################### :Copyright: GPLv3+ :Manual section: 1 Filename: network.so Requires: none SYNOPSIS ======== The network module tracks te state of the current network connection. Expression Functions ==================== NetInfo(Query[,Interface]) -------------------------- Function NetInfo queries the parameter of the connection on the network interface specified. If Interface is not specifies, NetInfo will query the interface of the defult gateway (if one exists). The queries supported are: "ip" IP address of the interface. "mask" Net mask of the interface. "cidr" Net mask in the CIDR notation. "gateway" The default gateway (not necessarily associated with the interface). "ip6" IPv6 IP address of the interface. "mask6" IPv6 netmask of the interfce. "gateway6" The default IPv6 gateway. "essid" ESSID of the wireless connection (if applicable for the interface). NetInfo returns a string value. NetStat(Query[,Interface]) -------------------------- Function NetStat queries statistics of the interface. If the interface isn't specified, it will be applied to the interface of the default gateway. The queries supported are: "rxrate" Receive data rate on the interface (in bps). "txrate" Transmit data rate on the interface (in bps). "signal" Signal strength of the wifi connection (if applicable). NetState returns a numeric value. Actions ======= None Triggers ======== The module defines one trigger "network" which is emitted whenever the interface data is changes (i.e. ip, netmask, default gateway, wifi essid). sfwbar-1.0~beta13/doc/sfwbar-pulse.1000066400000000000000000000052161450657570000173120ustar00rootroot00000000000000.\" Man page generated from reStructuredText. . . .nr rst2man-indent-level 0 . .de1 rstReportMargin \\$1 \\n[an-margin] level \\n[rst2man-indent-level] level margin: \\n[rst2man-indent\\n[rst2man-indent-level]] - \\n[rst2man-indent0] \\n[rst2man-indent1] \\n[rst2man-indent2] .. .de1 INDENT .\" .rstReportMargin pre: . RS \\$1 . nr rst2man-indent\\n[rst2man-indent-level] \\n[an-margin] . nr rst2man-indent-level +1 .\" .rstReportMargin post: .. .de UNINDENT . RE .\" indent \\n[an-margin] .\" old: \\n[rst2man-indent\\n[rst2man-indent-level]] .nr rst2man-indent-level -1 .\" new: \\n[rst2man-indent\\n[rst2man-indent-level]] .in \\n[rst2man-indent\\n[rst2man-indent-level]]u .. .TH "SFWBAR-PULSE" 1 "" "" "" .SH NAME sfwbar-pulse \- Sfwbar PulseAudio module .sp Filename: pulsectl.so .sp Requires: libpulse .SH SYNOPSIS .sp The PulseAudio module provides an interface to query and adjust the settings of the PulseAudio server. .SH EXPRESSION FUNCTIONS .SS Pulse(Query[,Interface]) .sp function Pulse queries the state of the Pulse server, the Query parameter specifies the information to query, the optional Interface parameter specifies the interface to which the query applies. If the Interface isn\(aqt given the query is applied to the current sink or the current source. The supported query types are: .INDENT 0.0 .TP .B \(dqsink\-volume\(dq the volume of a sink. .TP .B \(dqsink\-mute\(dq muted state of the sink. .TP .B \(dqsink\-icon\(dq sink icon. .TP .B \(dqsink\-form\(dq the form of the sink. .TP .B \(dqsink\-port\(dq the port name of the sink. .TP .B \(dqsink\-monitor\(dq name of the source monitor for the sink. .TP .B \(dqsource\-volume\(dq the volume of a source. .TP .B \(dqsource\-mute\(dq muted state of the source. .TP .B \(dqsource\-icon\(dq source icon. .TP .B \(dqsource\-form\(dq the form of the source. .TP .B \(dqsource\-port\(dq the port name of the source. .TP .B \(dqsource\-monitor\(dq name of the sink monitor for the source. .UNINDENT .SH ACTIONS .SS PulseCmd [Interface,],Command .sp Manipulate the state of the interface, if the interface isn\(aqt specified, the Command will be applied to the current interface. .INDENT 0.0 .TP .B \(dqsink\-volume +/\-X\(dq Adjust the volume of the sink by X%. .TP .B \(dqsink\-mute State\(dq Change the state of the sink, State can be On, Off or Toggle. .TP .B \(dqsource\-volume +/\-X\(dq Adjust the volume of the source by X%. .TP .B \(dqsource\-mute State\(dq Change the state of the source, State can be On, Off or Toggle. .UNINDENT .SH TRIGGERS .sp The module defines one trigger \(dqPulse\(dq which is emitted whenever the state of the pulse server changes. .SH COPYRIGHT GPLv3+ .\" Generated by docutils manpage writer. . sfwbar-1.0~beta13/doc/sfwbar-pulse.rst000066400000000000000000000034561450657570000177660ustar00rootroot00000000000000sfwbar-pulse ############ ######################## Sfwbar PulseAudio module ######################## :Copyright: GPLv3+ :Manual section: 1 Filename: pulsectl.so Requires: libpulse SYNOPSIS ======== The PulseAudio module provides an interface to query and adjust the settings of the PulseAudio server. Expression Functions ==================== Pulse(Query[,Interface]) ------------------------ function Pulse queries the state of the Pulse server, the Query parameter specifies the information to query, the optional Interface parameter specifies the interface to which the query applies. If the Interface isn't given the query is applied to the current sink or the current source. The supported query types are: "sink-volume" the volume of a sink. "sink-mute" muted state of the sink. "sink-icon" sink icon. "sink-form" the form of the sink. "sink-port" the port name of the sink. "sink-monitor" name of the source monitor for the sink. "source-volume" the volume of a source. "source-mute" muted state of the source. "source-icon" source icon. "source-form" the form of the source. "source-port" the port name of the source. "source-monitor" name of the sink monitor for the source. Actions ======= PulseCmd [Interface,],Command ----------------------------- Manipulate the state of the interface, if the interface isn't specified, the Command will be applied to the current interface. "sink-volume +/-X" Adjust the volume of the sink by X%. "sink-mute State" Change the state of the sink, State can be On, Off or Toggle. "source-volume +/-X" Adjust the volume of the source by X%. "source-mute State" Change the state of the source, State can be On, Off or Toggle. Triggers ======== The module defines one trigger "Pulse" which is emitted whenever the state of the pulse server changes. sfwbar-1.0~beta13/doc/sfwbar-widgets.1000066400000000000000000000103571450657570000176320ustar00rootroot00000000000000.\" Man page generated from reStructuredText. . . .nr rst2man-indent-level 0 . .de1 rstReportMargin \\$1 \\n[an-margin] level \\n[rst2man-indent-level] level margin: \\n[rst2man-indent\\n[rst2man-indent-level]] - \\n[rst2man-indent0] \\n[rst2man-indent1] \\n[rst2man-indent2] .. .de1 INDENT .\" .rstReportMargin pre: . RS \\$1 . nr rst2man-indent\\n[rst2man-indent-level] \\n[an-margin] . nr rst2man-indent-level +1 .\" .rstReportMargin post: .. .de UNINDENT . RE .\" indent \\n[an-margin] .\" old: \\n[rst2man-indent\\n[rst2man-indent-level]] .nr rst2man-indent-level -1 .\" new: \\n[rst2man-indent\\n[rst2man-indent-level]] .in \\n[rst2man-indent\\n[rst2man-indent-level]]u .. .TH "SFWBAR" "" "" .SH NAME SFWBar \- SFWBar - Widget Wizardry Manual .sp Sfwbar provides necessary features to develop reasonably sophisticated and efficient user defined widgets. There are four main components involved in developing a widget: .sp 1. Scanner sources \- to facilitate gathering of the information to display 1. Expression engine \- to manipulate the data sourced by the scanner 1. Layout widgets \- to display the data and results of computations 1. Actions \- to manipulate the state .SH WIDGET EFFICIENCY .sp The original rationale for development of the Sfwbar\(aqs predecessor was facilitation of efficient widget construction. A common approach to status bar widget implementation involves periodic execution of a shell script which reads data from output of commands or system files, parses the contents, performs necessary calculations or text manipulations on the data and outputs the result in a format useable by the widget. .sp The above approach can be very inefficient for information requiring high frequency of updates. The status bar executes a shell, which loads a script, which may in turn execute a number of other executables to source and manipulate the data. The outcome is a multitude of fork\(aqs and image loads, which are executed on a regular basis. This ultimately requires a trade\-off between the frequency of updates and the system load. .sp Sfwbar provides basic functionality that allows polling for data, extracting information from this data and manipulating it into output useable by status bar widgets. .SH BASIC STEPS .sp These are the basic steps involved in creating a widget: .SS GET DATA .sp Data can obtained from variety of sources. On a typical linux system, a lot of system information is exposed via /proc and /sys filesystems. On BSD systems this information would be available via a sysctl call. Some systems expose and update variety of information via sockets or have client utilities that can provide asynchronous status updates via stdout. As a last resort, we can fall back to periodic execution of a program that would output the information we need, although in some cases we can improve the efficientcy of this approach by using a status change notification from one of the more efficient methods. .SH EXTRACT DATAPOINTS .sp The data we obtain from various sources isn\(aqt always presented in a directly useable format. We may need to parse these data dumps to extract the data points we need. Sfwbar allows extracting iinfromation the data dump using regular expression or json parsers. I.e. if the /proc file contains several strings of text, sfwbar can identify a string matching a pattern, extract a segment of this string, using a regular expression capture buffer and save this outout in an internal variable. Sfwbar can extract multiple patterns from the same data dump and populate multiple variables. .SH MANIPULATE INFORMATION .sp Once infromation is parsed, we can manipulate it into a format we need for display. It can be something simple, such as rounding a number for decimal precision or a complex requiring flow control, mathematical calculations and text manipulation (i.e. calculating color gradients and generating an svg image on the fly). .SH VISUALIZE OUTPUT .sp Once we have the data in the format we need, we need to create a visual representation to be presented to a user. This can be a simple text string displayed on the status bar, an icon with a name derived from the data we gathered, an image we generated on the fly, a scale showing a fraction or a chart showing evolution of the data point over time. .\" Generated by docutils manpage writer. . sfwbar-1.0~beta13/doc/sfwbar-widgets.rst000066400000000000000000000132301450657570000202730ustar00rootroot00000000000000SFWBar ###### ############################### SFWBar - Widget Wizardry Manual ############################### Sfwbar provides necessary features to develop reasonably sophisticated and efficient user defined widgets. There are four main components involved in developing a widget: 1. Scanner sources - to facilitate gathering of the information to display 1. Expression engine - to manipulate the data sourced by the scanner 1. Layout widgets - to display the data and results of computations 1. Actions - to manipulate the state WIDGET EFFICIENCY ================= The original rationale for development of the Sfwbar's predecessor was facilitation of efficient widget construction. A common approach to status bar widget implementation involves periodic execution of a shell script which reads data from output of commands or system files, parses the contents, performs necessary calculations or text manipulations on the data and outputs the result in a format useable by the widget. The above approach can be very inefficient for information requiring high frequency of updates. The status bar executes a shell, which loads a script, which may in turn execute a number of other executables to source and manipulate the data. The outcome is a multitude of fork's and image loads, which are executed on a regular basis. This ultimately requires a trade-off between the frequency of updates and the system load. Sfwbar provides basic functionality that allows polling for data, extracting information from this data and manipulating it into output useable by status bar widgets. BASIC STEPS =========== These are the basic steps involved in creating a widget: GET DATA -------- Data can obtained from variety of sources. On a typical linux system, a lot of system information is exposed via /proc and /sys filesystems. On BSD systems this information would be available via a sysctl call. Some systems expose and update variety of information via sockets or have client utilities that can provide asynchronous status updates via stdout. As a last resort, we can fall back to periodic execution of a program that would output the information we need, although in some cases we can improve the efficientcy of this approach by using a status change notification from one of the more efficient methods. EXTRACT DATAPOINTS ------------------ The data we obtain from various sources isn't always presented in a directly useable format. We may need to parse these data dumps to extract the data points we need. Sfwbar allows extracting iinfromation the data dump using regular expression or json parsers. I.e. if the /proc file contains several strings of text, sfwbar can identify a string matching a pattern, extract a segment of this string, using a regular expression capture buffer and save this outout in an internal variable. Sfwbar can extract multiple patterns from the same data dump and populate multiple variables. MANIPULATE INFORMATION ---------------------- Once infromation is parsed, we can manipulate it into a format we need for display. It can be something simple, such as rounding a number for decimal precision or a complex requiring flow control, mathematical calculations and text manipulation (i.e. calculating color gradients and generating an svg image on the fly). VISUALIZE OUTPUT ---------------- Once we have the data in the format we need, we need to create a visual representation to be presented to a user. This can be a simple text string displayed on the status bar, an icon with a name derived from the data we gathered, an image we generated on the fly, a scale showing a fraction or a chart showing evolution of the data point over time. CONSTRUCTING A SIMPLE WIDGET ============================ Here's an example of a simple widget: :: scanner { file("/proc/meminfo") { MemTotal = RegEx("^MemTotal:[\t ]*([0-9]+)[\t ]") MemFree = RegEx("^MemFree:[\t ]*([0-9]+)[\t ]") MemCache = RegEx( "^Cached:[\t ]*([0-9]+)[\t ]") MemBuff = Regex("^Buffers:[\t ]*([0-9]+)[\t ]") } } layout { label { interval = 500 value = Str((MemTotal-MemFree-MemCache-MemBuff)/MemTotal*100) + "%" } } This widget periodically reads file ``/proc/meminfo``, extracts numeric values for MemTotal, MemFree, Cached and Buffers lines, computes a percentage of memory in use, formats it into a percentage string and displays it as a label. An important performance question to consider is how often will the file be read. The scanner will process a file whenever an expression accesses the variables defined in that file. In the example above, the label is updated on an interval of 500 milliseconds. This would trigger a read of ``/proc/meminfo`` twice a second. All parsing of the file and manipulations of data are performed internall by Sfwbar, so this shouldn't be too onerous on most systems. It's worth noting that the label itself will only be updated if the result of the expression changes. Since the expression rounds the memory utilization figure to a nearest percentage point, the label will only change if the memory utilization changes by one percent. USING TRIGGERS ============== Using periodic updates is a reasonable approach when the information the widget needs is only available on request (i.e. we can query memory or CPU utilization at any time to get the most up to date information. For a lot of other datapoints, it may be more efficient to listen for specific events and update the data when these events occur. This is implemented via triggers. These come from various sources. I.e. modules, compositor IPC's, sockets, RT signals or executables that produce output on specific events. Triggers can be used to initiate widget updates or to execute specific actions. sfwbar-1.0~beta13/doc/sfwbar-xkbmap.1000066400000000000000000000024541450657570000174450ustar00rootroot00000000000000.\" Man page generated from reStructuredText. . . .nr rst2man-indent-level 0 . .de1 rstReportMargin \\$1 \\n[an-margin] level \\n[rst2man-indent-level] level margin: \\n[rst2man-indent\\n[rst2man-indent-level]] - \\n[rst2man-indent0] \\n[rst2man-indent1] \\n[rst2man-indent2] .. .de1 INDENT .\" .rstReportMargin pre: . RS \\$1 . nr rst2man-indent\\n[rst2man-indent-level] \\n[an-margin] . nr rst2man-indent-level +1 .\" .rstReportMargin post: .. .de UNINDENT . RE .\" indent \\n[an-margin] .\" old: \\n[rst2man-indent\\n[rst2man-indent-level]] .nr rst2man-indent-level -1 .\" new: \\n[rst2man-indent\\n[rst2man-indent-level]] .in \\n[rst2man-indent\\n[rst2man-indent-level]]u .. .TH "SFWBAR-XKBMAP" 1 "" "" "" .SH NAME sfwbar-xkbmap \- Sfwbar xkbcommon layout module .sp Filename: xkbmap.so .sp Requires: libxkbregistry .SH SYNOPSIS .sp The XkbMap module provides an interface to convert between keyboard layout types. .SH EXPRESSION FUNCTIONS .SS XkbMap(Name,SourceType,DestType) .sp Function XkbMap converts between various name types for an XkbLayout. The function converts layout name Name from SourceType to DestType, the supported types are: \(dqdescription\(dq,\(dqname\(dq,\(dqvariant\(dq and \(dqbrief\(dq. .SH ACTIONS .sp None .SH TRIGGERS .sp None .SH COPYRIGHT GPLv3+ .\" Generated by docutils manpage writer. . sfwbar-1.0~beta13/doc/sfwbar-xkbmap.rst000066400000000000000000000012441450657570000201110ustar00rootroot00000000000000sfwbar-xkbmap ############# ############################## Sfwbar xkbcommon layout module ############################## :Copyright: GPLv3+ :Manual section: 1 Filename: xkbmap.so Requires: libxkbregistry SYNOPSIS ======== The XkbMap module provides an interface to convert between keyboard layout types. Expression Functions ==================== XkbMap(Name,SourceType,DestType) -------------------------------- Function XkbMap converts between various name types for an XkbLayout. The function converts layout name Name from SourceType to DestType, the supported types are: "description","name","variant" and "brief". Actions ======= None Triggers ======== None sfwbar-1.0~beta13/doc/sfwbar.1000066400000000000000000001143751450657570000161730ustar00rootroot00000000000000.\" Man page generated from reStructuredText. . . .nr rst2man-indent-level 0 . .de1 rstReportMargin \\$1 \\n[an-margin] level \\n[rst2man-indent-level] level margin: \\n[rst2man-indent\\n[rst2man-indent-level]] - \\n[rst2man-indent0] \\n[rst2man-indent1] \\n[rst2man-indent2] .. .de1 INDENT .\" .rstReportMargin pre: . RS \\$1 . nr rst2man-indent\\n[rst2man-indent-level] \\n[an-margin] . nr rst2man-indent-level +1 .\" .rstReportMargin post: .. .de UNINDENT . RE .\" indent \\n[an-margin] .\" old: \\n[rst2man-indent\\n[rst2man-indent-level]] .nr rst2man-indent-level -1 .\" new: \\n[rst2man-indent\\n[rst2man-indent-level]] .in \\n[rst2man-indent\\n[rst2man-indent-level]]u .. .TH "SFWBAR" 1 "" "" .SH NAME SFWBar \- S* Floating Window taskBar .SH SYNOPSIS .nf \fBsfwbar\fP [options] .fi .sp .SH DESCRIPTION .sp \fBSFWBar\fP is a taskbar for wayland compositors. Originally written for Sway, it should work with any compositor supporting layer\-shell protocol. SFWBar assists in handling of floating windows on a wayland desktop. It provides a taskbar, a pager, a task switcher, a system tray, a floating window placement engine, a simple widget set for display data extracted from various system files. SFWBar can work with any wayland compositor supporting layer shell protocol. Taskbar and switcher require either sway or wlr\-foreign\-toplevel protocol support. Placer and pager require sway. .SH OPTIONS .sp SFWBar executable can be invoked with the following options: .INDENT 0.0 .TP .B \-f | \-\-config Specify a filename of a configuration file .TP .B \-c | \-\-css Specify a filename of a css file .TP .B \-s | \-\-socket Specify a location of sway ipc socket .TP .B \-m | \-\-monitor Specify a monitor to display the bar on (\(dq\-m list\(dq to list available monitors) .TP .B \-b | \-\-bar_id Specify a sway bar_id on which sfwbar will listen for status changes .UNINDENT .SH CONFIGURATION .sp SFWBar reads configuration from a config file (sfwbar.config by default). The program checks users XDG config directory (usually ~/.config/sfwbar/) for this file, followed by system xdg data directories. Additionally, user can specify a location and a name of the config file using \fB\-f\fP command line option. .sp Appearance of the program can be specified using CSS properties, these are sourced either from the css section of the main configuration file or from a file with a .css extension with the same base name as the config file located in the same directory as the config file. The name of the css file can be also specified using \fB\-c\fP option. .sp The config file consists of the following top level sections: .SS Placer .sp Placer section enables intelligent placement of new floating windows. If enabled the program will first attempt to place the window in a location, where it won\(aqt overlap with other windows. If such location doesn\(aqt exist, the window will be placed in a cascading pattern from top\-left to bottom\-right. The Placer declaration accepts parameters \(dqxstep\(dq and \(dqystep\(dq that specify the steps in the window cascade. These are specified in percentage of the desktop dimensions. The cascade placement will start at a location specified by \(dqxorigin\(dq \(dqyorigin\(dq parameters. I.e.: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C placer { xorigin = 5 yorigin = 5 xstep = 5 ystep = 5 children = false } .ft P .fi .UNINDENT .UNINDENT .sp \(dqchildren\(dq parameter specifies if new windows opened by a program with other windows already opened should be placed. These are usually dialog windows and SFWBar won\(aqt place them by default. If the placer section is not present in the file, SFWBar will let the compositor determine the locations for new windows. .SS Task Switcher .sp Task switcher implements a keyboard shortcut to cycle focus across windows (i.e. Alt\-Tab). The action is triggered upon receiving a change in a bar hidden_state property or signal SIGUSR1. This can be configured in Sway, via one of the following bindings: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C bindsym Alt\-Tab bar hidden_state toggle or bindsym Alt\-Tab exec killall \-SIGUSR1 sfwbar .ft P .fi .UNINDENT .UNINDENT .sp (for non\-sway compositors, use SIGUSR1 trigger) .sp NixOS + Hyprland (probably other non\-sway compositors) use: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C bind = ALT, Tab, exec, killall \-SIGUSR1 .sfwbar\-wrapped .ft P .fi .UNINDENT .UNINDENT .sp Task switcher is configured in the \(dqswitcher\(dq section of the configuration file. The following parameters are accepted: .INDENT 0.0 .TP .B interval an timeout after the last task switch event after which the selected window is activated .TP .B labels [true|false] display window titles in the task list .TP .B icons [true|false] display window icons in the task list .TP .B cols a number of columns in the task list .TP .B css css code applicable to the switcher grid. You can specify more detailed css code in the main CSS file. Using style name #switcher for the task switcher window and the main grid and names #switcher_normal and #switcher_active for inactive and active window representations respectively. .UNINDENT .SS Layout .sp Defines the layout of the taskbar. The layout holds a set of widgets. Widgets can be nested in case of a \fBgrid\fP widget, which acts as a container. .sp The following widget types are supported: .INDENT 0.0 .TP .B taskbar a special widget displaying a list of all floating windows. (requires a compositor supporting wlr\-foreign\-toplevel protocol or i3 ipc) .TP .B pager a special widget displaying a list of all workspaces. (requires a compositor supporting wlr\-foreign\-toplevel protocol or i3 ipc) .TP .B tray a special widget displaying a list of tray icons received via status notifier item interface .TP .B grid a layout grid capable of containing other widgets. You can use these to further subdivide each cell of the main grid and arrange items within it. .TP .B label a label displaying text sourced from an expression. Labels accept pango markup to further theme text withing them. .TP .B scale a progress bar with a progress value specified by an expression .TP .B chart a chart plotting the value of the expression over time .TP .B image display an icon or an image from a file. The name of an icon or a file is specified by an expression and can change dynamically. .TP .B button add a clickable button with an icon/image. .UNINDENT .sp Each widget is placed within the parent grid. By default, widgets are placed next to the previous widget along the \(dqdirection\(dq of the grid (left to right by default). You can specify widget\(aqs positions within a grid by using a property \(dqloc(x,y[,w,h])\(dq with the first two parameters specifying the location of the widget within the parent grid and the last two parameters specifying the widget dimensions in grid cells: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C layout \(dqid\(dq { label { style = \(dqmystyle\(dq value = SwapUsed / SwapTotal + \(dq%\(dq loc(2,1,1,1) } } .ft P .fi .UNINDENT .UNINDENT .sp The optional \(dqid\(dq string of the layout, specifies the bar to populate and can control positioning of the grid within a bar using syntax of \(dqname:position\(dq, valid positions are start, center and end. This allows placement of some widgets in the center of the bar. In case of a single bar, the name of a bar can be omitted, i.e. \(dq:center\(dq. External widgets can be included in layout using the following syntax: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C layout { include(\(dqMyWidget.widget\(dq) } .ft P .fi .UNINDENT .UNINDENT .sp The above will include all scanner variables data and widget sub\-layout from file MyWidget.widget .sp Grid widgets can contain other widgets, these are declared within the grid definition i.e. .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C grid { css = \(dq* { border: none }\(dq label \(dqid\(dq { ... } } .ft P .fi .UNINDENT .UNINDENT .sp Widgets can optionally have unique id\(aqs assigned to them in order to allow manipulating them in the future. Widgets can have the following properties: .INDENT 0.0 .TP .B value an expression specifying the value to display. This can be a static value (i.e. \fB\(dqstring\(dq\fP or \fB1\fP) or an expression (i.e. \fB\(dqValue is:\(dq + $MyString\fP or \fB2 * MyNumber.val\fP). See \fBexpressions\fP section for more detail. For \fBLabel\fP widgets value tells text to display. For \fBScale\fP widgets it specifies a fraction to display. For \fBChart\fP widgets it specifies a fraction of the next datapoint. For \fBImage\fP widgets and buttons it provides an icon or an image file name. .TP .B style a style name for the widget. Styles can be used in CSS to theme widgets. Multiple widgets can have the same style. A style name can be used in css using gtk+ named widget convention, i.e. \fBlabel#mystyle\fP\&. Style property can be set to an expression to change styles in response to changes in system parameters. .TP .B tooltip sets a tooltip for a widget. A tooltip can be a static value or an expression. In case of the latter, the tooltip will be dynamically updated every time it pops up. .TP .B interval widget update frequency in milliseconds.. .TP .B trigger trigger on which event updates. Triggers are emitted by Client sources a widget should not have both an interval and a trigger specified. (if both are specified, interval is ignored and trigger is used). .TP .B css additional css properties for the widget. These properties will only apply to the widget in question. .TP .B action an action to execute upon interaction with a widget. Actions can be attached to any widget. Multiple actions can be attached to various pointer events. The notation is \fBaction[] = \fP\&. Event values are 1,2,3 or LeftClick, MiddleClick or RightClick respectively. For mouse scroll events, use values 4,5,6,7 or ScrollUp, ScrollDown, ScrollLeft and ScrollRight respectively. If no index is specified the action is attached to a left mouse button click. Additionallly, modifiers can be specified using the notation of \fB[Modifier+]Index\fP\&. I.e. \fBaction[Ctrl+LeftClick]\fP\&. The following modifiers supported: Shift, Ctrl, Mod1, Mod2, Mod3, Mod4, Mod5i, Super, Hyper and Meta. Multiple modifiers can be added, i.e. \fBaction[Ctrl+Shift+ScrollUp]\fP\&. action[0] will be executed on startup. You can use this action to set initial configuration for a widget. See \fBActions\fP section for more details on how actions are specified. .UNINDENT .sp \fBTaskbar\fP widget may contain the following options .INDENT 0.0 .TP .B labels [true|false] an indicator whether to display an application title within the taskbar .TP .B icons [true|false] an indicator whether to display application icons within the taskbar .TP .B filter_output [true|false] This property is deprecated, please use \fBfilter\fP instead. specifies whether taskbar should only list windows present on the same output as the taskbar .TP .B filter [output|workspace] Specifies whether taskbar should only list windows present on the same output or workspace as the taskbar itself. .TP .B title_width set maximum width of an application title in characters .TP .B sort [true|false] setting of whether taskbar items should be sorted. If the items are not sorted, user can sort them manually via drag\-and\-drop mechanism. Items are sorted by default, set this to false to enable drag\-and\-drop. .TP .B rows a number of rows in a taskbar. .TP .B cols a number of columns in a taskbar. If both rows and cols are specified, rows will be used. If neither is specified, the default is rows=1 .TP .B group [true|false] if set to true, the taskbar items will be grouped by app_id, the main taskbar will contain one item per app_id with an icon and a label set to app_id. On over, it will popup a \(dqgroup taskbar\(dq containing items for individual windows. You can specify taskbar parameters for the group taskbars using group prefix, i.e. \fBgroup cols = 1\fP\&. The properties supported for groups are cols, rows, style, css, title_width, labels, icons. .UNINDENT .sp \fBPager\fP widget may contain the following options .INDENT 0.0 .TP .B preview [true|false] specifies whether workspace previews are displayed on mouse hover over pager buttons .TP .B sort [true|false] setting of whether pager items should be sorted. If the items are not sorted, user can sort them manually via drag\-and\-drop mechanism. Items are sorted by default, set this to false to enable drag\-and\-drop. .TP .B numeric [true|false] if true, the workspaces will be sorted as numbers, otherwise they will be sorted as strings (defaults to true). .TP .B pins a list of \(dqpinned\(dq workspaces. These will show up in the pager even if the workspace is empty. .TP .B rows a number of rows in a pager. .TP .B cols a number of columns in a pager. If both rows and cols are specified, rows will be used. If neither is specified, the default is rows=1 .UNINDENT .sp \fBtray\fP widget may contain the following options .INDENT 0.0 .TP .B rows a number of rows in a pager. .TP .B cols a number of columns in a pager. If both rows and cols are specified, rows will be used. If neither is specified, the default is rows=1 .TP .B sort [true|false] setting of whether tray items should be sorted. If the items are not sorted, user can sort them manually via drag\-and\-drop mechanism. Items are sorted by default, set this to false to enable drag\-and\-drop. .UNINDENT .sp \fBpopup\fP window may contain the following options .INDENT 0.0 .TP .B AutoClose [true|false] specify whether the popup window should close if user clicks anywhere outside of the window. .UNINDENT .SS PopUp .sp Popup windows can be defined the same way as layouts. The only difference is that popup\(aqs are not part of a bar and will not be displayed by default. Instead they are displayed when a PopUp action is invoked on a widget. i.e.: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C PopUp \(dqMyPopup\(dq { label { value = \(dqtest\(dq } } Layout { label { value = \(dqclick me\(dq action = PopUp \(dqMyPopup\(dq } } .ft P .fi .UNINDENT .UNINDENT .sp The PopUp action toggles visibility of the popup window. I.e. the first time it\(aqs invoked, the window will pop up and on the second invocation it will pop down. As a result it should be safe to bind the PopUp to multiple widgets. .SS Menus .sp User defined menus can be attached to any widget (see \fBaction\fP widget property). Menus are defined using a Menu section in the config file. The example syntax is as following: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C menuclear(\(dqmenu_name\(dq) menu (\(dqmenu_name\(dq) { item(\(dqitem1\(dq, Exec \(dqcommand\(dq) separator submenu(\(dqsub\(dq,\(dqmysubmenu\(dq) { item(\(dqitem2\(dq, SwayCmd \(dqfocus next\(dq) } } .ft P .fi .UNINDENT .UNINDENT .sp Command MenuClear deletes any existing items from a menu. Each menu has a name used to link the menu to the widget action and a list of menu items. If a menu with the same name is defined more than once, the items from subsequence declarations will be appended to the original menu. If you want to re\-define the menu, use MenuClear action to clear the original menu. .sp The following menu items are supported: .INDENT 0.0 .TP .B item an actionable menu item. This item has two parameters, the first one is a label, the second is an action to execute when the item is activated. See \fBActions\fP section for more details on supported actions. .TP .B separator a menu separator. This item has no parameters .TP .B submenu attach a submenu. The first parameter parameter is a label to display in the parent menu, the second optional parameter is a menu name, if a menu name is assigned, further items can be added to a submenu as to any other menu. .UNINDENT .SS Triggers .sp Triggers are emitted in response to various events, such as compositor state changes, real time signals or notifications from modules. Some triggers can be defined as part of the configuration (i.e. SocketClient or ExecClient scanner sources), others are built in, or defined in modules. .sp Built\-in triggers are: .TS center; |l|l|. _ T{ SIGRTMIN+X T} T{ RT signal SIGRTMIN+X has been received (X is a number) T} _ T{ sway T} T{ Data has been received on SwayClient scanner source T} _ T{ mpd T} T{ Data has been received on MpdClient scanner source T} _ T{ \-connected T} T{ an output has been connected (i.e. eDP\-1\-connected) T} _ T{ \-disconnected T} T{ an output has been disconnected T} _ .TE .SS Actions .sp Actions can be attached to click and scroll events for any widget or to items within a menu. Actions can be conditional on a state of a window or a widget they refer to and some actions may require a parameter. Conditions are specified in square brackets prior to the action i.e. \fB[Minimized]\fP and can be inverted using \fB!\fP or joined using \fB|\fP i.e. \fB[!Minimized | Focused]\fP\&. All conditions on the list must be satisfied. Supported conditions are: \fBMinimized\fP, \fBMaximized\fP, \fBFocused\fP, \fBFullScreen\fP and \fBUserState\fP .sp Actions can be activated upon receipt of a trigger from one of the client type sources, using TriggerAction top\-level keyword. I.e. .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C TriggerAction \(dqmytrigger\(dq, Exec \(dqMyCommand\(dq .ft P .fi .UNINDENT .UNINDENT .sp Parameters are specified as strings immediately following the relevant action. I.e. \fBMenu \(dqWindowOps\(dq\fP\&. Some actions apply to a window, if the action is attached to taskbar button, the action will be applied to a window referenced by the button, otherwise, it will apply to the currently focused window. The following action types are supported: .INDENT 0.0 .TP .B Config Process a snippet of configuration file. This action permits changing the bar configuration on the fly .TP .B Exec execute a shell command .TP .B Function [,] Execute a function. Accepts an optional address, to invoke a function on a specific widget. .TP .B Menu open a menu with a given name .TP .B MenuClear delete a menu with a given name (This is useful if you want to generate menus dynamically via PipeRead and would like to delete a previously generated menu) .TP .B PipeRead Process a snippet of configuration sourced from an output of a shell command .TP .B SwayCmd send a command over Sway IPC .TP .B SwayWinCmd send a command over Sway IPC applicable to a current window .TP .B MpdCmd send a command to Music Player Daemon .TP .B ClientSend , send a string to a client. The string will be written to client\(aqs standard input for execClient clients or written into a socket for socketClient\(aqs. The first parameter is the client id, the second is the string to send. .TP .B SwitcherEvent trigger a switcher event, this action will bring up the switcher window and cycle the focus either forward or back based on the argument. The string argument can be either \(dqfoward\(dq or \(dqback\(dq. If the argument is omitted, the focus will cycle forward. .TP .B SetMonitor [,] move bar to a given monitor. Bar_name string specifies a bar to move. monitor name can be prefixed by \(dqstatic:\(dq, i.e. \(dqstatic:eDP\-1\(dq, if this is set and the specified monitor doesn\(aqt exist or gets disconnected, the bar will not jump to another montior, but will be hidden and won\(aqt reappear until the monitor is reconnected. .TP .B SetLayer [,] move bar to a specified layer (supported parameters are \(dqtop\(dq, \(dqbottom\(dq, \(dqbackground\(dq and \(dqoverlay\(dq. .TP .B SetBarSize [,] set size of the bar (width for top or bottom bar, height for left or right bar). The argument is a string. I.e. \(dq800\(dq for 800 pixels or \(dq50%\(dq for 50% of screen size .TP .B SetBarSensor [], Specify whether the bar should be hidden once the pointer leaves the bar window. Once hidden, the bar will popup again if the pointer touches the sensor located along the screen edge along which the bar is placed. String specifies the bar pop\-down delay in milliseconds. .TP .B SetBarID specify bar ID to listen on for mode and hidden_state signals. If no bar ID is specified, SfwBar will listen to signals on all IDs .TP .B SetExclusiveZone [,] specify exclusive zone policy for the bar window. Acceptable values are \(dqauto\(dq, \(dq\-1\(dq, \(dq0\(dq or positive integers. These have meanings in line with exclusive zone setting in the layer shell protocol. Default value is \(dqauto\(dq .TP .B SetValue [,] set the value of the widget. This action applies to the widget from which the action chain has been invoked. I.e. a widget may popup a menu, which in turn will call a function, which executed SetValue, the SetValue will still ac upon the widget that popped up the menu. .TP .B SetStyle [,] set style name for a widget .TP .B SetTooltip [,] set tooltip text for a widget .TP .B UserState Set boolean user state on a widget. Valid values are \(dqOn\(dq or \(dqOff\(dq. .TP .B Focus set window to focused .TP .B Close close a window .TP .B Minimize minimize a window (send to scratchpad in sway) .TP .B UnMinimize unset a minimized state for the window .TP .B Maximize maximize a window (set fullscreen in sway) .TP .B UnMaximize unset a maximized state for the window .UNINDENT .SS Functions .sp Functions are sequences of actions. They are used when multiple actions need to be execute on a single triggeer. A good example of this functionality is dynamically constructed menus generated by an external script: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C function(\(dqfancy_menu\(dq) { MenuClear \(dqdynamic_menu\(dq PipeRead \(dq$HOME/bin/buildmenu.sh\(dq Menu \(dqdynamic_menu\(dq } .ft P .fi .UNINDENT .UNINDENT .sp The above example clears a menu, executes a script that builds a menu again and opens the resulting menu. .sp Function \(dqSfwBarInit\(dq executed on startup. You can use this functions to set initial parameters for the bar, such as default monitor and layer. .SS Scanner .sp SFWBar widgets display data obtained from various sources. These can be files or output of commands. .sp Each source section contains one or more variables that SFWBar will poll periodically and populate with the data parsed from the source. The sources and variables linked to them as configured in the section \fBscanner\fP .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C scanner { file(\(dq/proc/swaps\(dq,NoGlob) { SwapTotal = RegEx(\(dq[\et ]([0\-9]+)\(dq) SwapUsed = RegEx(\(dq[\et ][0\-9]+[\et ]([0\-9]+)\(dq) } exec(\(dqgetweather.sh\(dq) { $WeatherTemp = Json(\(dq.forecast.today.degrees\(dq) } } .ft P .fi .UNINDENT .UNINDENT .sp Each declaration within the \fBscanner\fP section specifies a source. This can be one of the following: .INDENT 0.0 .TP .B File Read data from a file .TP .B Exec Read data from an output of a shell command .TP .B ExecClient Read data from an executable, this source will read a burst of data using it to populate the variables and emit a trigger event once done. This source accepts two parameters, command to execute and an id. The id is used to address the socket via ClientSend and to identify a trigger emitted upon variable updates. USE RESPONSIBLY: If a trigger causes the client to receive new data (i.e. by triggering a ClientSend command that in turn triggers response from the source, you can end up with an infinite loop. .TP .B SocketClient Read data from a socket, this source will read a bust of data using it to populate the variables and emit a trigger event once done. This source accepts two parameters, a socket address and an id. The id is used to address the socket via ClientSend and to identify a trigger emitted upon variable updates. USE RESPONSIBLY: If a trigger causes the client to receive new data (i.e. by triggering a ClientSend command that in turn triggers response from the source, you can end up with an infinite loop. .TP .B MpdClient Read data from Music Player Daemon IPC (data is polled whenever MPD responds to an \(aqidle player\(aq event). MpdClient emits trigger \(dqmpd\(dq .TP .B SwayClient Receive updates on Sway state, updates are the json objects sent by sway, wrapped into an object with a name of the event i.e. \fBwindow: { sway window change object }\fP SwayClient emits trigger \(dqsway\(dq .UNINDENT .sp The file source also accepts further optional arguments specifying how scanner should handle the source, these can be: .INDENT 0.0 .TP .B NoGlob specifies that SFWBar shouldn\(aqt attempt to expand the pattern in the file name. If this flag is not specified, the file source will attempt to read from all files matching a filename pattern. .TP .B CheckTime indicates that the program should only update the variables from this file when file modification date/time changes. .UNINDENT .sp \fBVariables\fP are extracted from sources using parsers, currently the following parsers are supported: .INDENT 0.0 .TP .B Grab([Aggregator]) specifies that the data is copied from the file verbatim .TP .B RegEx(Pattern[,Aggregator]) extracts data using a regular expression parser, the variable is assigned data from the first capture buffer .TP .B Json(Path[,Aggregator]) extracts data from a json structure. The path starts with a separator character, which is followed by a path with elements separated by the same character. The path can contain numbers to indicate array indices i.e. \fB\&.data.node.1.string\fP and key checks to filter arrays, i.e. \fB\&.data.node.[key=\(dqblah\(dq].value\fP .UNINDENT .sp Optional aggregators specify how multiple occurrences of numeric data are treated. The following aggregators are supported: .INDENT 0.0 .TP .B First Variable should be set to the first occurrence of the pattern in the source .TP .B Last Variable should be set to the last occurrence of the pattern in the source .TP .B Sum Variable should be set to the sum of all occurrences of the pattern in the source .TP .B Product Variable should be set to the product of all occurrences of the pattern in the source .UNINDENT .sp For string variables, Sum and Product aggregators are treated as Last. .SS Global Options .INDENT 0.0 .TP .B Theme Override a Gtk theme to name specified. .TP .B DisownMinimized Disassociate windows from their workplaces when they are minimized. If this option is set, selecting a minimize window will unminimize it on the active workplace. If set to False (default), the window will be unminimzied to it\(aqs last workplace. This option only applies to Sway and Hyprland comositors .TP .B FilterTitle Any windows with titles matching a regular expression will not be shown on the taskbar or switcher. .TP .B FilterAppId Any windows with appids matching a regular expression will not be shown on the taskbar or switcher. .TP .B TriggerAction , execute an action when a trigger is emitted. Trigger is a string, an action is any valid action, as described in the Actions section. .UNINDENT .SS EXPRESSIONS .sp Values in widgets can contain basic arithmetic and string manipulation expressions. These allow transformation of data obtained by the scanner before it is displayed by the widgets. .sp The numeric operations are: .TS center; |l|l|. _ T{ Operation T} T{ Description T} _ T{ \fB+\fP T} T{ addition T} _ T{ \fB\-\fP T} T{ subtraction T} _ T{ \fB*\fP T} T{ multiplication T} _ T{ \fB/\fP T} T{ division T} _ T{ \fB%\fP T} T{ remainder of an integer division T} _ T{ \fB>\fP T} T{ greater than T} _ T{ \fB>=\fP T} T{ greater than or equal T} _ T{ \fB<\fP T} T{ less than T} _ T{ \fB>=\fP T} T{ less than or equal T} _ T{ \fB=\fP T} T{ equal T} _ T{ \fBVal\fP T} T{ convert a string into a number, the argument is a string or a string expression to convert. T} _ T{ \fBIf\fP T} T{ conditional: If(condition,expr1,expr2) T} _ T{ \fBCached\fP T} T{ get last value from a variable without updating it: Cached(identifier) T} _ T{ \fBIdent\fP T} T{ Check if an identifier exists either as a variable or a function T} _ .TE .sp The string operations are: .TS center; |l|l|. _ T{ Operation T} T{ Description T} _ T{ \fB+\fP T} T{ concatenate strings i.e. \fB\(dq\(aqString\(aq+$Var\(dq\fP\&. T} _ T{ \fBMid\fP T} T{ extract substring i.e. \fBMid($Var,2,5)\fP T} _ T{ \fBExtract\fP T} T{ extract a regex pattern i.e. \fBExtract($Var,\(aqFindThis: (GrabThat)\(aq)\fP T} _ T{ \fBStr\fP T} T{ convert a number into a string, the first argument is a number (or a numeric expression), the second argument is decimal precision. If precision is omitted, the number is rounded to the nearest integer. T} _ T{ \fBPad\fP T} T{ pad a string to be n characters long, the first parameter is a string to pad, the second is the desired number of characters, if the number is negative, the string is padded at the end, if positive, the string is padded at the front. The third optional string parameter specifies the character to pad the string with. T} _ T{ \fBUpper\fP T} T{ Convert a string to upper case T} _ T{ \fBLower\fP T} T{ Convert a string to lower case T} _ T{ \fBLookup\fP T} T{ lookup a numeric value within a list of tuplets, the function call is \fBLookup(Value, Threshold1, String1, ..., DefaultString)\fP\&. The function checks value against a thresholds and returns a String associated with the highest threshold matched by the Value. If the Value is lower than all thresholds, DefaultString is returned. Thresholds in the function call must be in decreasing order. T} _ T{ \fBMap\fP T} T{ Match a string within a list of tuplets, the usage is: \fBMap(Value, Match1,String\(ga,...,DefaultString)\fP\&. THe function will match Value against all Match strings and will return a corresponding String, if none of the Match strings match, the function will return DefaultString. T} _ .TE .sp In addition the following query functions are supported .TS center; |l|l|. _ T{ Function T} T{ Description T} _ T{ \fBTime\fP T} T{ get current time as a string, the first optional argument specifies the format, the second argument specifies a timezone. Return a string T} _ T{ \fBDisk\fP T} T{ get disk utilization data. You need to specify a mount point as a first argument and data field as a second. The supported data fields are \(dqtotal\(dq, \(dqavail\(dq, \(dqfree\(dq, \(dq%avail\(dq, \(dq%free\(dq or \(dq%used\(dq. Returns a number. T} _ T{ \fBActiveWin\fP T} T{ get the title of currently focused window. Returns a string. T} _ T{ \fBGtkEvent\fP T} T{ Get the location of an event that triggered the action. This function is only applicable in action command expressions where an action is called as a result of button click. The function returns location of the click within the widget. The value is returned as percentage of the widget width or height. Acceptable arguments are \(dqX\(dq,\(dqY\(dq and \(dqDir\(dq. X and Y select an axis for which to return the event location, Dir returns the event location along the widget direction property. T} _ T{ \fBWidgetID\fP T} T{ Obtain an ID of the current widget (i.e. a widget in respect to which the expression is being evaluated. T} _ .TE .sp Each numeric variable contains four values .INDENT 0.0 .TP .B \&.val current value of the variable .TP .B \&.pval previous value of the variable .TP .B \&.time time elapsed between observing .pval and .val .TP .B \&.age time elapsed since variable was last updated .TP .B \&.count a number of time the pattern has been matched during the last scan .UNINDENT .sp By default, the value of the variable is the value of .val. String variables are prefixed with $, i.e. $StringVar The following string operation are supported. For example: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C $MyString + Str((MyValue \- MyValue.pval)/MyValue.time),2) .ft P .fi .UNINDENT .UNINDENT .sp User defined expression macros are supported via top\-level \fBdefine\fP keyword. I.e. .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C define MyExpr = VarA + VarB * VarC + Val($Complex) \&... value = Str(MyExpr,2) .ft P .fi .UNINDENT .UNINDENT .sp The above will expand the expression into: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C value = Str(VarA + VarB * VarC + Val($Complex),2) .ft P .fi .UNINDENT .UNINDENT .sp Macro\(aqs don\(aqt have types, as they perform substitution before the expression is evaluated. .sp Intermediate variables can be declared using a toplevel \fBset\fP keyword I.e. .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C set MyExpr = VarA + VarB * VarC + Val($Complex \&... value = Str(MyExpr,2) .ft P .fi .UNINDENT .UNINDENT .sp In the above example, value of the MyExpr variable will be calculated and the result will be used in computing the value expression. Intermediate varibles have type and have all of the fields of a scan variable (i.e. val, pval, time etc). They can be used the same way as scan variables. .SH MISCELLANEOUS .sp If the icon is missing for a specific program in the taskbar or switcher, it is likely due to an missing icon or application not setting app_id correctly. You can check app_id\(aqs of running programs by running sfwbar \-d \-g app_id. if app_id is present, you need to add an icon with the appropriate name to your icon theme. If it\(aqs blank, you can try mapping it from the program\(aqs title (please note that the title may change during runtime, so matching it can be tricky). Mapping is supported via top\-level \fBMapAppId\fP keyword. I.e. .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C MapAppId app_id, pattern .ft P .fi .UNINDENT .UNINDENT .sp where app_id is the desired app_id and pattern is a regular expression to match the title against. .sp If you are using an XWayland app, they usually do not have an \fIapp_id\fP set. If an icon is not showing, you can add your icon to the following locations: 1. \fI$HOME/.icons\fP 2. One of the directories listed in \fI$XDG_DATA_DIRS/icons\fP 3. \fI/usr/share/pixmaps\fP 4. Location of the main config file currently in use 5. \fI$XDG_CONFIG_HOME/sfwbar/\fP .sp If an \fIapp_id\fP is not set, and sway is being used, sfwbar will fallback to using the \fIinstance\fP in the \fIwindow\-properties\fP\&. .sp You can find the \fIapp_id\fP that is being used with sfwbar by using the \fIsfwbar \-d \-g app_id\fP command, which will show a list of running applications if your compositor supports the wlr\-foreign\-toplevel protocol (i.e. labwc, wayfire, sway): \fB\(ga 14:49:25.41 app_id: \(aqjetbrains\-clion\(aq, title \(aqsfwbar – pager.c\(aq \(ga\fP .sp Alternatively your desktop environment might have a command to display a list: \- Sway: \fIswaymsg \-t get_tree\fP \- Hyperland: \fIhyprctl \-j clients\fP .sp When using \fIswaymsg \-t get_tree\fP, with CLion this will show the following: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C \(dqwindow_properties\(dq: { \(dqclass\(dq: \(dqjetbrains\-clion\(dq, \(dqinstance\(dq: \(dqjetbrains\-clion\(dq, \(dqtitle\(dq: \(dqsfwbar – trayitem.c\(dq, \(dqtransient_for\(dq: null, \(dqwindow_type\(dq: \(dqnormal\(dq } .ft P .fi .UNINDENT .UNINDENT .sp So we can put an icon called jetbrains\-clion.svg (or other formats, see the [Arch wiki](\fI\%https://wiki.archlinux.org/title/desktop_entries#Icons\fP)) for information about file formats. .SH CSS STYLE .sp SFWBar uses gtk+ widgets and can accept all css properties supported by gtk+. SFWBar widgets correspond to gtk+ widgets as following: .TS center; |l|l|l|. _ T{ SFWBar widget T} T{ gtk+ widget T} T{ css class T} _ T{ label T} T{ GtkLabel T} T{ label T} _ T{ image T} T{ GtkImage T} T{ image T} _ T{ button T} T{ GtkButton T} T{ button T} _ T{ scale T} T{ GtkProgressBar T} T{ progressbar, trough, progress T} _ .TE .sp Taskbar, Pager, Tray and Switcher use combinations of these widgets and can be themed using gtk+ nested css convention, i.e. \fBgrid#taskbar button { ... }\fP (this example assumes you assigned \fBstyle = taskbar\fP to your taskbar widget). .sp In addition to standard gtk+ css properties SFWBar implements several additional properties. These are: .TS center; |l|l|. _ T{ property T} T{ description T} _ T{ \-GtkWidget\-align T} T{ specify text alignment for a label, defined as a fraction. (0 = left aligned, 1 = right aligned, 0.5 = centered) T} _ T{ \-GtkWidget\-ellipsize T} T{ specify whether a text in a label should be ellipsized if it\(aqs too long to fit in allocated space T} _ T{ \-GtkWidget\-direction T} T{ specify a direction for a widget. For scale, it\(aqs a direction towards which scale grows. For a grid, it\(aqs a direction in which a new widget is position relative to the last placed widget. For a window it\(aqs an edge along which the bar is positioned. Possible values [top|bottom|left|right] T} _ T{ \-GtkWidget\-max\-width T} T{ Limit maximum width of a widget (in pixels) T} _ T{ \-GtkWidget\-max\-height T} T{ Limit maximum height of a widget (in pixels) T} _ T{ \-GtkWidget\-hexpand T} T{ specify if a widget should expand horizontally to occupy available space. [true|false] T} _ T{ \-GtkWidget\-vexpand T} T{ as above, for vertical expansion. T} _ T{ \-GtkWidget\-halign T} T{ Horizontally align widget within any free space allocated to it, values supported are: fill, start, end, center and baseline. The last vertically aligns widgets to align text within. T} _ T{ \-GtkWidget\-valign T} T{ Vertically align widget. T} _ T{ \-GtkWidget\-visible T} T{ Control visibility of a widget. If set to false, widget will be hidden. T} _ T{ \-ScaleImage\-color T} T{ Specify a color to repaint an image with. The image will be painted with this color using image\(aqs alpha channel as a mask. The color\(aqs own alpha value can be used to tint an image. T} _ T{ \-ScaleImage\-symbolic T} T{ Render an image as a symbolic icon. If set to true, the image will be re\-colored to the gtk theme foreground color, preserving the image alpha channel. This property is ignored if \-ScaleImage\-color is specified. T} _ .TE .sp Taskbar and pager buttons are assigned the following styles .TS center; |l|l|. _ T{ style name T} T{ description T} _ T{ sfwbar T} T{ toplevel bar window T} _ T{ layout T} T{ top level layout grid T} _ T{ taskbar_normal T} T{ taskbar button for a window T} _ T{ taskbar_active T} T{ taskbar button for currently focused window T} _ T{ pager_normal T} T{ pager button for a workspace T} _ T{ pager_visible T} T{ pager button for a visible workspace T} _ T{ pager_focused T} T{ pager button for a currently focused workspace T} _ T{ switcher T} T{ switcher window and top level grid T} _ T{ switcher_active T} T{ switcher active window representation T} _ T{ switcher_normal T} T{ switcher inactive window representation T} _ T{ tray T} T{ tray menus and menu items T} _ T{ tray_active T} T{ active tray icon T} _ T{ tray_attention T} T{ tray icon requiring user attention T} _ T{ tray_passive T} T{ passive tray icon T} _ T{ menu_item T} T{ menu items (each contains an image and a label) T} _ .TE .sp For example you can style top level grid using \fBgrid#layout { }\fP\&. .SH COPYRIGHT GPLv3+ .\" Generated by docutils manpage writer. . sfwbar-1.0~beta13/doc/sfwbar.rst000066400000000000000000001112531450657570000166330ustar00rootroot00000000000000SFWBar ###### ########################## S* Floating Window taskBar ########################## :Copyright: GPLv3+ :Manual section: 1 SYNOPSIS ======== | **sfwbar** [options] DESCRIPTION =========== **SFWBar** is a taskbar for wayland compositors. Originally written for Sway, it should work with any compositor supporting layer-shell protocol. SFWBar assists in handling of floating windows on a wayland desktop. It provides a taskbar, a pager, a task switcher, a system tray, a floating window placement engine, a simple widget set for display data extracted from various system files. SFWBar can work with any wayland compositor supporting layer shell protocol. Taskbar and switcher require either sway or wlr-foreign-toplevel protocol support. Placer and pager require sway. OPTIONS ======= SFWBar executable can be invoked with the following options: -f | --config Specify a filename of a configuration file -c | --css Specify a filename of a css file -s | --socket Specify a location of sway ipc socket -m | --monitor Specify a monitor to display the bar on ("-m list" to list available monitors) -b | --bar_id Specify a sway bar_id on which sfwbar will listen for status changes CONFIGURATION ============= SFWBar reads configuration from a config file (sfwbar.config by default). The program checks users XDG config directory (usually ~/.config/sfwbar/) for this file, followed by system xdg data directories. Additionally, user can specify a location and a name of the config file using ``-f`` command line option. Appearance of the program can be specified using CSS properties, these are sourced either from the css section of the main configuration file or from a file with a .css extension with the same base name as the config file located in the same directory as the config file. The name of the css file can be also specified using ``-c`` option. The config file consists of the following top level sections: Placer ------ Placer section enables intelligent placement of new floating windows. If enabled the program will first attempt to place the window in a location, where it won't overlap with other windows. If such location doesn't exist, the window will be placed in a cascading pattern from top-left to bottom-right. The Placer declaration accepts parameters "xstep" and "ystep" that specify the steps in the window cascade. These are specified in percentage of the desktop dimensions. The cascade placement will start at a location specified by "xorigin" "yorigin" parameters. I.e.:: placer { xorigin = 5 yorigin = 5 xstep = 5 ystep = 5 children = false } "children" parameter specifies if new windows opened by a program with other windows already opened should be placed. These are usually dialog windows and SFWBar won't place them by default. If the placer section is not present in the file, SFWBar will let the compositor determine the locations for new windows. Task Switcher ------------- Task switcher implements a keyboard shortcut to cycle focus across windows (i.e. Alt-Tab). The action is triggered upon receiving a change in a bar hidden_state property or signal SIGUSR1. This can be configured in Sway, via one of the following bindings: :: bindsym Alt-Tab bar hidden_state toggle or bindsym Alt-Tab exec killall -SIGUSR1 sfwbar (for non-sway compositors, use SIGUSR1 trigger) NixOS + Hyprland (probably other non-sway compositors) use: :: bind = ALT, Tab, exec, killall -SIGUSR1 .sfwbar-wrapped Task switcher is configured in the "switcher" section of the configuration file. The following parameters are accepted: interval an timeout after the last task switch event after which the selected window is activated labels [true|false] display window titles in the task list icons [true|false] display window icons in the task list cols a number of columns in the task list css css code applicable to the switcher grid. You can specify more detailed css code in the main CSS file. Using style name #switcher for the task switcher window and the main grid and names #switcher_normal and #switcher_active for inactive and active window representations respectively. Layout ------ Defines the layout of the taskbar. The layout holds a set of widgets. Widgets can be nested in case of a ``grid`` widget, which acts as a container. The following widget types are supported: taskbar a special widget displaying a list of all floating windows. (requires a compositor supporting wlr-foreign-toplevel protocol or i3 ipc) pager a special widget displaying a list of all workspaces. (requires a compositor supporting wlr-foreign-toplevel protocol or i3 ipc) tray a special widget displaying a list of tray icons received via status notifier item interface grid a layout grid capable of containing other widgets. You can use these to further subdivide each cell of the main grid and arrange items within it. label a label displaying text sourced from an expression. Labels accept pango markup to further theme text withing them. scale a progress bar with a progress value specified by an expression chart a chart plotting the value of the expression over time image display an icon or an image from a file. The name of an icon or a file is specified by an expression and can change dynamically. button add a clickable button with an icon/image. Each widget is placed within the parent grid. By default, widgets are placed next to the previous widget along the "direction" of the grid (left to right by default). You can specify widget's positions within a grid by using a property "loc(x,y[,w,h])" with the first two parameters specifying the location of the widget within the parent grid and the last two parameters specifying the widget dimensions in grid cells:: layout "id" { label { style = "mystyle" value = SwapUsed / SwapTotal + "%" loc(2,1,1,1) } } The optional "id" string of the layout, specifies the bar to populate and can control positioning of the grid within a bar using syntax of "name:position", valid positions are start, center and end. This allows placement of some widgets in the center of the bar. In case of a single bar, the name of a bar can be omitted, i.e. ":center". External widgets can be included in layout using the following syntax: :: layout { include("MyWidget.widget") } The above will include all scanner variables data and widget sub-layout from file MyWidget.widget Grid widgets can contain other widgets, these are declared within the grid definition i.e. :: grid { css = "* { border: none }" label "id" { ... } } Widgets can optionally have unique id's assigned to them in order to allow manipulating them in the future. Widgets can have the following properties: value an expression specifying the value to display. This can be a static value (i.e. ``"string"`` or ``1``) or an expression (i.e. ``"Value is:" + $MyString`` or ``2 * MyNumber.val``). See ``expressions`` section for more detail. For ``Label`` widgets value tells text to display. For ``Scale`` widgets it specifies a fraction to display. For ``Chart`` widgets it specifies a fraction of the next datapoint. For ``Image`` widgets and buttons it provides an icon or an image file name. style a style name for the widget. Styles can be used in CSS to theme widgets. Multiple widgets can have the same style. A style name can be used in css using gtk+ named widget convention, i.e. ``label#mystyle``. Style property can be set to an expression to change styles in response to changes in system parameters. tooltip sets a tooltip for a widget. A tooltip can be a static value or an expression. In case of the latter, the tooltip will be dynamically updated every time it pops up. interval widget update frequency in milliseconds.. trigger trigger on which event updates. Triggers are emitted by Client sources a widget should not have both an interval and a trigger specified. (if both are specified, interval is ignored and trigger is used). css additional css properties for the widget. These properties will only apply to the widget in question. action an action to execute upon interaction with a widget. Actions can be attached to any widget. Multiple actions can be attached to various pointer events. The notation is ``action[] = ``. Event values are 1,2,3 or LeftClick, MiddleClick or RightClick respectively. For mouse scroll events, use values 4,5,6,7 or ScrollUp, ScrollDown, ScrollLeft and ScrollRight respectively. If no index is specified the action is attached to a left mouse button click. Additionallly, modifiers can be specified using the notation of ``[Modifier+]Index``. I.e. ``action[Ctrl+LeftClick]``. The following modifiers supported: Shift, Ctrl, Mod1, Mod2, Mod3, Mod4, Mod5i, Super, Hyper and Meta. Multiple modifiers can be added, i.e. ``action[Ctrl+Shift+ScrollUp]``. action[0] will be executed on startup. You can use this action to set initial configuration for a widget. See ``Actions`` section for more details on how actions are specified. ``Taskbar`` widget may contain the following options labels [true|false] an indicator whether to display an application title within the taskbar icons [true|false] an indicator whether to display application icons within the taskbar filter_output [true|false] This property is deprecated, please use ``filter`` instead. specifies whether taskbar should only list windows present on the same output as the taskbar filter [output|workspace] Specifies whether taskbar should only list windows present on the same output or workspace as the taskbar itself. title_width set maximum width of an application title in characters sort [true|false] setting of whether taskbar items should be sorted. If the items are not sorted, user can sort them manually via drag-and-drop mechanism. Items are sorted by default, set this to false to enable drag-and-drop. rows a number of rows in a taskbar. cols a number of columns in a taskbar. If both rows and cols are specified, rows will be used. If neither is specified, the default is rows=1 group [true|false] if set to true, the taskbar items will be grouped by app_id, the main taskbar will contain one item per app_id with an icon and a label set to app_id. On over, it will popup a "group taskbar" containing items for individual windows. You can specify taskbar parameters for the group taskbars using group prefix, i.e. ``group cols = 1``. The properties supported for groups are cols, rows, style, css, title_width, labels, icons. ``Pager`` widget may contain the following options preview [true|false] specifies whether workspace previews are displayed on mouse hover over pager buttons sort [true|false] setting of whether pager items should be sorted. If the items are not sorted, user can sort them manually via drag-and-drop mechanism. Items are sorted by default, set this to false to enable drag-and-drop. numeric [true|false] if true, the workspaces will be sorted as numbers, otherwise they will be sorted as strings (defaults to true). pins a list of "pinned" workspaces. These will show up in the pager even if the workspace is empty. rows a number of rows in a pager. cols a number of columns in a pager. If both rows and cols are specified, rows will be used. If neither is specified, the default is rows=1 ``tray`` widget may contain the following options rows a number of rows in a pager. cols a number of columns in a pager. If both rows and cols are specified, rows will be used. If neither is specified, the default is rows=1 sort [true|false] setting of whether tray items should be sorted. If the items are not sorted, user can sort them manually via drag-and-drop mechanism. Items are sorted by default, set this to false to enable drag-and-drop. ``popup`` window may contain the following options AutoClose [true|false] specify whether the popup window should close if user clicks anywhere outside of the window. PopUp ----- Popup windows can be defined the same way as layouts. The only difference is that popup's are not part of a bar and will not be displayed by default. Instead they are displayed when a PopUp action is invoked on a widget. i.e.: :: PopUp "MyPopup" { label { value = "test" } } Layout { label { value = "click me" action = PopUp "MyPopup" } } The PopUp action toggles visibility of the popup window. I.e. the first time it's invoked, the window will pop up and on the second invocation it will pop down. As a result it should be safe to bind the PopUp to multiple widgets. Menus ----- User defined menus can be attached to any widget (see ``action`` widget property). Menus are defined using a Menu section in the config file. The example syntax is as following: :: menuclear("menu_name") menu ("menu_name") { item("item1", Exec "command") separator submenu("sub","mysubmenu") { item("item2", SwayCmd "focus next") } } Command MenuClear deletes any existing items from a menu. Each menu has a name used to link the menu to the widget action and a list of menu items. If a menu with the same name is defined more than once, the items from subsequence declarations will be appended to the original menu. If you want to re-define the menu, use MenuClear action to clear the original menu. The following menu items are supported: item an actionable menu item. This item has two parameters, the first one is a label, the second is an action to execute when the item is activated. See ``Actions`` section for more details on supported actions. separator a menu separator. This item has no parameters submenu attach a submenu. The first parameter parameter is a label to display in the parent menu, the second optional parameter is a menu name, if a menu name is assigned, further items can be added to a submenu as to any other menu. Triggers -------- Triggers are emitted in response to various events, such as compositor state changes, real time signals or notifications from modules. Some triggers can be defined as part of the configuration (i.e. SocketClient or ExecClient scanner sources), others are built in, or defined in modules. Built-in triggers are: ===================== ========================================================= SIGRTMIN+X RT signal SIGRTMIN+X has been received (X is a number) sway Data has been received on SwayClient scanner source mpd Data has been received on MpdClient scanner source -connected an output has been connected (i.e. eDP-1-connected) -disconnected an output has been disconnected ===================== ========================================================= Actions ------- Actions can be attached to click and scroll events for any widget or to items within a menu. Actions can be conditional on a state of a window or a widget they refer to and some actions may require a parameter. Conditions are specified in square brackets prior to the action i.e. ``[Minimized]`` and can be inverted using ``!`` or joined using ``|`` i.e. ``[!Minimized | Focused]``. All conditions on the list must be satisfied. Supported conditions are: ``Minimized``, ``Maximized``, ``Focused``, ``FullScreen`` and ``UserState`` Actions can be activated upon receipt of a trigger from one of the client type sources, using TriggerAction top-level keyword. I.e. :: TriggerAction "mytrigger", Exec "MyCommand" Parameters are specified as strings immediately following the relevant action. I.e. ``Menu "WindowOps"``. Some actions apply to a window, if the action is attached to taskbar button, the action will be applied to a window referenced by the button, otherwise, it will apply to the currently focused window. The following action types are supported: Config Process a snippet of configuration file. This action permits changing the bar configuration on the fly Exec execute a shell command Function [,] Execute a function. Accepts an optional address, to invoke a function on a specific widget. Menu open a menu with a given name MenuClear delete a menu with a given name (This is useful if you want to generate menus dynamically via PipeRead and would like to delete a previously generated menu) PipeRead Process a snippet of configuration sourced from an output of a shell command SwayCmd send a command over Sway IPC SwayWinCmd send a command over Sway IPC applicable to a current window MpdCmd send a command to Music Player Daemon ClientSend , send a string to a client. The string will be written to client's standard input for execClient clients or written into a socket for socketClient's. The first parameter is the client id, the second is the string to send. SwitcherEvent trigger a switcher event, this action will bring up the switcher window and cycle the focus either forward or back based on the argument. The string argument can be either "foward" or "back". If the argument is omitted, the focus will cycle forward. SetMonitor [,] move bar to a given monitor. Bar_name string specifies a bar to move. monitor name can be prefixed by "static:", i.e. "static:eDP-1", if this is set and the specified monitor doesn't exist or gets disconnected, the bar will not jump to another montior, but will be hidden and won't reappear until the monitor is reconnected. SetLayer [,] move bar to a specified layer (supported parameters are "top", "bottom", "background" and "overlay". SetBarSize [,] set size of the bar (width for top or bottom bar, height for left or right bar). The argument is a string. I.e. "800" for 800 pixels or "50%" for 50% of screen size SetBarSensor [], Specify whether the bar should be hidden once the pointer leaves the bar window. Once hidden, the bar will popup again if the pointer touches the sensor located along the screen edge along which the bar is placed. String specifies the bar pop-down delay in milliseconds. SetBarID specify bar ID to listen on for mode and hidden_state signals. If no bar ID is specified, SfwBar will listen to signals on all IDs SetExclusiveZone [,] specify exclusive zone policy for the bar window. Acceptable values are "auto", "-1", "0" or positive integers. These have meanings in line with exclusive zone setting in the layer shell protocol. Default value is "auto" SetValue [,] set the value of the widget. This action applies to the widget from which the action chain has been invoked. I.e. a widget may popup a menu, which in turn will call a function, which executed SetValue, the SetValue will still ac upon the widget that popped up the menu. SetStyle [,] set style name for a widget SetTooltip [,] set tooltip text for a widget UserState Set boolean user state on a widget. Valid values are "On" or "Off". Focus set window to focused Close close a window Minimize minimize a window (send to scratchpad in sway) UnMinimize unset a minimized state for the window Maximize maximize a window (set fullscreen in sway) UnMaximize unset a maximized state for the window Functions --------- Functions are sequences of actions. They are used when multiple actions need to be execute on a single triggeer. A good example of this functionality is dynamically constructed menus generated by an external script: :: function("fancy_menu") { MenuClear "dynamic_menu" PipeRead "$HOME/bin/buildmenu.sh" Menu "dynamic_menu" } The above example clears a menu, executes a script that builds a menu again and opens the resulting menu. Function "SfwBarInit" executed on startup. You can use this functions to set initial parameters for the bar, such as default monitor and layer. Scanner ------- SFWBar widgets display data obtained from various sources. These can be files or output of commands. Each source section contains one or more variables that SFWBar will poll periodically and populate with the data parsed from the source. The sources and variables linked to them as configured in the section ``scanner`` :: scanner { file("/proc/swaps",NoGlob) { SwapTotal = RegEx("[\t ]([0-9]+)") SwapUsed = RegEx("[\t ][0-9]+[\t ]([0-9]+)") } exec("getweather.sh") { $WeatherTemp = Json(".forecast.today.degrees") } } Each declaration within the ``scanner`` section specifies a source. This can be one of the following: File Read data from a file Exec Read data from an output of a shell command ExecClient Read data from an executable, this source will read a burst of data using it to populate the variables and emit a trigger event once done. This source accepts two parameters, command to execute and an id. The id is used to address the socket via ClientSend and to identify a trigger emitted upon variable updates. USE RESPONSIBLY: If a trigger causes the client to receive new data (i.e. by triggering a ClientSend command that in turn triggers response from the source, you can end up with an infinite loop. SocketClient Read data from a socket, this source will read a bust of data using it to populate the variables and emit a trigger event once done. This source accepts two parameters, a socket address and an id. The id is used to address the socket via ClientSend and to identify a trigger emitted upon variable updates. USE RESPONSIBLY: If a trigger causes the client to receive new data (i.e. by triggering a ClientSend command that in turn triggers response from the source, you can end up with an infinite loop. MpdClient Read data from Music Player Daemon IPC (data is polled whenever MPD responds to an 'idle player' event). MpdClient emits trigger "mpd" SwayClient Receive updates on Sway state, updates are the json objects sent by sway, wrapped into an object with a name of the event i.e. ``window: { sway window change object }`` SwayClient emits trigger "sway" The file source also accepts further optional arguments specifying how scanner should handle the source, these can be: NoGlob specifies that SFWBar shouldn't attempt to expand the pattern in the file name. If this flag is not specified, the file source will attempt to read from all files matching a filename pattern. CheckTime indicates that the program should only update the variables from this file when file modification date/time changes. ``Variables`` are extracted from sources using parsers, currently the following parsers are supported: Grab([Aggregator]) specifies that the data is copied from the file verbatim RegEx(Pattern[,Aggregator]) extracts data using a regular expression parser, the variable is assigned data from the first capture buffer Json(Path[,Aggregator]) extracts data from a json structure. The path starts with a separator character, which is followed by a path with elements separated by the same character. The path can contain numbers to indicate array indices i.e. ``.data.node.1.string`` and key checks to filter arrays, i.e. ``.data.node.[key="blah"].value`` Optional aggregators specify how multiple occurrences of numeric data are treated. The following aggregators are supported: First Variable should be set to the first occurrence of the pattern in the source Last Variable should be set to the last occurrence of the pattern in the source Sum Variable should be set to the sum of all occurrences of the pattern in the source Product Variable should be set to the product of all occurrences of the pattern in the source For string variables, Sum and Product aggregators are treated as Last. Global Options -------------- Theme Override a Gtk theme to name specified. DisownMinimized Disassociate windows from their workplaces when they are minimized. If this option is set, selecting a minimize window will unminimize it on the active workplace. If set to False (default), the window will be unminimzied to it's last workplace. This option only applies to Sway and Hyprland comositors FilterTitle Any windows with titles matching a regular expression will not be shown on the taskbar or switcher. FilterAppId Any windows with appids matching a regular expression will not be shown on the taskbar or switcher. TriggerAction , execute an action when a trigger is emitted. Trigger is a string, an action is any valid action, as described in the Actions section. EXPRESSIONS ----------- Values in widgets can contain basic arithmetic and string manipulation expressions. These allow transformation of data obtained by the scanner before it is displayed by the widgets. The numeric operations are: ============ ==================================================================== Operation Description ============ ==================================================================== ``+`` addition ``-`` subtraction ``*`` multiplication ``/`` division ``%`` remainder of an integer division ``>`` greater than ``>=`` greater than or equal ``<`` less than ``>=`` less than or equal ``=`` equal ``Val`` convert a string into a number, the argument is a string or a string expression to convert. ``If`` conditional: If(condition,expr1,expr2) ``Cached`` get last value from a variable without updating it: Cached(identifier) ``Ident`` Check if an identifier exists either as a variable or a function ============ ==================================================================== The string operations are: ============ =================================================================== Operation Description ============ =================================================================== ``+`` concatenate strings i.e. ``"'String'+$Var"``. ``Mid`` extract substring i.e. ``Mid($Var,2,5)`` ``Extract`` extract a regex pattern i.e. ``Extract($Var,'FindThis: (GrabThat)')`` ``Str`` convert a number into a string, the first argument is a number (or a numeric expression), the second argument is decimal precision. If precision is omitted, the number is rounded to the nearest integer. ``Pad`` pad a string to be n characters long, the first parameter is a string to pad, the second is the desired number of characters, if the number is negative, the string is padded at the end, if positive, the string is padded at the front. The third optional string parameter specifies the character to pad the string with. ``Upper`` Convert a string to upper case ``Lower`` Convert a string to lower case ``Lookup`` lookup a numeric value within a list of tuplets, the function call is ``Lookup(Value, Threshold1, String1, ..., DefaultString)``. The function checks value against a thresholds and returns a String associated with the highest threshold matched by the Value. If the Value is lower than all thresholds, DefaultString is returned. Thresholds in the function call must be in decreasing order. ``Map`` Match a string within a list of tuplets, the usage is: ``Map(Value, Match1,String`,...,DefaultString)``. THe function will match Value against all Match strings and will return a corresponding String, if none of the Match strings match, the function will return DefaultString. ============ =================================================================== In addition the following query functions are supported ============= ================================================================= Function Description ============= ================================================================= ``Time`` get current time as a string, the first optional argument specifies the format, the second argument specifies a timezone. Return a string ``Disk`` get disk utilization data. You need to specify a mount point as a first argument and data field as a second. The supported data fields are "total", "avail", "free", "%avail", "%free" or "%used". Returns a number. ``ActiveWin`` get the title of currently focused window. Returns a string. ``GtkEvent`` Get the location of an event that triggered the action. This function is only applicable in action command expressions where an action is called as a result of button click. The function returns location of the click within the widget. The value is returned as percentage of the widget width or height. Acceptable arguments are "X","Y" and "Dir". X and Y select an axis for which to return the event location, Dir returns the event location along the widget direction property. ``WidgetID`` Obtain an ID of the current widget (i.e. a widget in respect to which the expression is being evaluated. ============= ================================================================= Each numeric variable contains four values .val current value of the variable .pval previous value of the variable .time time elapsed between observing .pval and .val .age time elapsed since variable was last updated .count a number of time the pattern has been matched during the last scan By default, the value of the variable is the value of .val. String variables are prefixed with $, i.e. $StringVar The following string operation are supported. For example: :: $MyString + Str((MyValue - MyValue.pval)/MyValue.time),2) User defined expression macros are supported via top-level ``define`` keyword. I.e. :: define MyExpr = VarA + VarB * VarC + Val($Complex) ... value = Str(MyExpr,2) The above will expand the expression into: :: value = Str(VarA + VarB * VarC + Val($Complex),2) Macro's don't have types, as they perform substitution before the expression is evaluated. Intermediate variables can be declared using a toplevel ``set`` keyword I.e. :: set MyExpr = VarA + VarB * VarC + Val($Complex ... value = Str(MyExpr,2) In the above example, value of the MyExpr variable will be calculated and the result will be used in computing the value expression. Intermediate varibles have type and have all of the fields of a scan variable (i.e. val, pval, time etc). They can be used the same way as scan variables. Miscellaneous ============= If the icon is missing for a specific program in the taskbar or switcher, it is likely due to an missing icon or application not setting app_id correctly. You can check app_id's of running programs by running sfwbar -d -g app_id. if app_id is present, you need to add an icon with the appropriate name to your icon theme. If it's blank, you can try mapping it from the program's title (please note that the title may change during runtime, so matching it can be tricky). Mapping is supported via top-level ``MapAppId`` keyword. I.e. :: MapAppId app_id, pattern where app_id is the desired app_id and pattern is a regular expression to match the title against. If you are using an XWayland app, they usually do not have an `app_id` set. If an icon is not showing, you can add your icon to the following locations: 1. `$HOME/.icons` 2. One of the directories listed in `$XDG_DATA_DIRS/icons` 3. `/usr/share/pixmaps` 4. Location of the main config file currently in use 5. `$XDG_CONFIG_HOME/sfwbar/` If an `app_id` is not set, and sway is being used, sfwbar will fallback to using the `instance` in the `window-properties`. You can find the `app_id` that is being used with sfwbar by using the `sfwbar -d -g app_id` command, which will show a list of running applications if your compositor supports the wlr-foreign-toplevel protocol (i.e. labwc, wayfire, sway): ``` 14:49:25.41 app_id: 'jetbrains-clion', title 'sfwbar – pager.c' ``` Alternatively your desktop environment might have a command to display a list: - Sway: `swaymsg -t get_tree` - Hyperland: `hyprctl -j clients` When using `swaymsg -t get_tree`, with CLion this will show the following: :: "window_properties": { "class": "jetbrains-clion", "instance": "jetbrains-clion", "title": "sfwbar – trayitem.c", "transient_for": null, "window_type": "normal" } So we can put an icon called jetbrains-clion.svg (or other formats, see the [Arch wiki](https://wiki.archlinux.org/title/desktop_entries#Icons)) for information about file formats. CSS Style ========= SFWBar uses gtk+ widgets and can accept all css properties supported by gtk+. SFWBar widgets correspond to gtk+ widgets as following: ============= =============== =============== SFWBar widget gtk+ widget css class ============= =============== =============== label GtkLabel label image GtkImage image button GtkButton button scale GtkProgressBar progressbar, trough, progress ============= =============== =============== Taskbar, Pager, Tray and Switcher use combinations of these widgets and can be themed using gtk+ nested css convention, i.e. ``grid#taskbar button { ... }`` (this example assumes you assigned ``style = taskbar`` to your taskbar widget). In addition to standard gtk+ css properties SFWBar implements several additional properties. These are: ===================== ============= property description ===================== ============= -GtkWidget-align specify text alignment for a label, defined as a fraction. (0 = left aligned, 1 = right aligned, 0.5 = centered) -GtkWidget-ellipsize specify whether a text in a label should be ellipsized if it's too long to fit in allocated space -GtkWidget-direction specify a direction for a widget. For scale, it's a direction towards which scale grows. For a grid, it's a direction in which a new widget is position relative to the last placed widget. For a window it's an edge along which the bar is positioned. Possible values [top|bottom|left|right] -GtkWidget-max-width Limit maximum width of a widget (in pixels) -GtkWidget-max-height Limit maximum height of a widget (in pixels) -GtkWidget-hexpand specify if a widget should expand horizontally to occupy available space. [true|false] -GtkWidget-vexpand as above, for vertical expansion. -GtkWidget-halign Horizontally align widget within any free space allocated to it, values supported are: fill, start, end, center and baseline. The last vertically aligns widgets to align text within. -GtkWidget-valign Vertically align widget. -GtkWidget-visible Control visibility of a widget. If set to false, widget will be hidden. -ScaleImage-color Specify a color to repaint an image with. The image will be painted with this color using image's alpha channel as a mask. The color's own alpha value can be used to tint an image. -ScaleImage-symbolic Render an image as a symbolic icon. If set to true, the image will be re-colored to the gtk theme foreground color, preserving the image alpha channel. This property is ignored if -ScaleImage-color is specified. ===================== ============= Taskbar and pager buttons are assigned the following styles ===================== ============= style name description ===================== ============= sfwbar toplevel bar window layout top level layout grid taskbar_normal taskbar button for a window taskbar_active taskbar button for currently focused window pager_normal pager button for a workspace pager_visible pager button for a visible workspace pager_focused pager button for a currently focused workspace switcher switcher window and top level grid switcher_active switcher active window representation switcher_normal switcher inactive window representation tray tray menus and menu items tray_active active tray icon tray_attention tray icon requiring user attention tray_passive passive tray icon menu_item menu items (each contains an image and a label) ===================== ============= For example you can style top level grid using ``grid#layout { }``. sfwbar-1.0~beta13/icons/000077500000000000000000000000001450657570000151605ustar00rootroot00000000000000sfwbar-1.0~beta13/icons/misc/000077500000000000000000000000001450657570000161135ustar00rootroot00000000000000sfwbar-1.0~beta13/icons/misc/comp.svg000066400000000000000000000035571450657570000176040ustar00rootroot00000000000000 sfwbar-1.0~beta13/icons/misc/cpu.svg000066400000000000000000000174421450657570000174330ustar00rootroot00000000000000 sfwbar-1.0~beta13/icons/misc/fan.svg000066400000000000000000000211651450657570000174050ustar00rootroot00000000000000 sfwbar-1.0~beta13/icons/misc/fforward.svg000066400000000000000000000015271450657570000204530ustar00rootroot00000000000000 sfwbar-1.0~beta13/icons/misc/lan.svg000066400000000000000000000034231450657570000174100ustar00rootroot00000000000000 sfwbar-1.0~beta13/icons/misc/lock.svg000066400000000000000000000020721450657570000175650ustar00rootroot00000000000000 sfwbar-1.0~beta13/icons/misc/missing.svg000066400000000000000000000027611450657570000203130ustar00rootroot00000000000000 sfwbar-1.0~beta13/icons/misc/music-player-symbolic.svg000066400000000000000000000034501450657570000230670ustar00rootroot00000000000000 sfwbar-1.0~beta13/icons/misc/play.svg000066400000000000000000000011731450657570000176030ustar00rootroot00000000000000 sfwbar-1.0~beta13/icons/misc/rewind.svg000066400000000000000000000015241450657570000201260ustar00rootroot00000000000000 sfwbar-1.0~beta13/icons/misc/stop.svg000066400000000000000000000011651450657570000176240ustar00rootroot00000000000000 sfwbar-1.0~beta13/icons/misc/unlock.svg000066400000000000000000000021161450657570000201270ustar00rootroot00000000000000 sfwbar-1.0~beta13/icons/sfwbar.svg000066400000000000000000000027611450657570000171730ustar00rootroot00000000000000 sfwbar-1.0~beta13/icons/weather/000077500000000000000000000000001450657570000166175ustar00rootroot00000000000000sfwbar-1.0~beta13/icons/weather/LICENSE000066400000000000000000000020621450657570000176240ustar00rootroot00000000000000The MIT License (MIT) Copyright (c) 2015-2017 Yr 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. sfwbar-1.0~beta13/icons/weather/clearsky_day.svg000066400000000000000000000025721450657570000220200ustar00rootroot00000000000000 sfwbar-1.0~beta13/icons/weather/clearsky_night.svg000066400000000000000000000013551450657570000223520ustar00rootroot00000000000000 sfwbar-1.0~beta13/icons/weather/clearsky_polartwilight.svg000066400000000000000000000035461450657570000241360ustar00rootroot00000000000000 sfwbar-1.0~beta13/icons/weather/cloudy.svg000066400000000000000000000025351450657570000206440ustar00rootroot00000000000000 sfwbar-1.0~beta13/icons/weather/fair_day.svg000066400000000000000000000055421450657570000211240ustar00rootroot00000000000000 sfwbar-1.0~beta13/icons/weather/fair_night.svg000066400000000000000000000043251450657570000214560ustar00rootroot00000000000000 sfwbar-1.0~beta13/icons/weather/fair_polartwilight.svg000066400000000000000000000065161450657570000232420ustar00rootroot00000000000000 sfwbar-1.0~beta13/icons/weather/fog.svg000066400000000000000000000035461450657570000201230ustar00rootroot00000000000000 sfwbar-1.0~beta13/icons/weather/heavyrain.svg000066400000000000000000000041111450657570000213230ustar00rootroot00000000000000 sfwbar-1.0~beta13/icons/weather/heavyrainandthunder.svg000066400000000000000000000053211450657570000234040ustar00rootroot00000000000000 sfwbar-1.0~beta13/icons/weather/heavyrainshowers_day.svg000066400000000000000000000072411450657570000236020ustar00rootroot00000000000000 sfwbar-1.0~beta13/icons/weather/heavyrainshowers_night.svg000066400000000000000000000060421450657570000241340ustar00rootroot00000000000000 sfwbar-1.0~beta13/icons/weather/heavyrainshowers_polartwilight.svg000066400000000000000000000102151450657570000257110ustar00rootroot00000000000000 sfwbar-1.0~beta13/icons/weather/heavyrainshowersandthunder_day.svg000066400000000000000000000104271450657570000256570ustar00rootroot00000000000000 sfwbar-1.0~beta13/icons/weather/heavyrainshowersandthunder_night.svg000066400000000000000000000072301450657570000262110ustar00rootroot00000000000000 sfwbar-1.0~beta13/icons/weather/heavyrainshowersandthunder_polartwilight.svg000066400000000000000000000114031450657570000277660ustar00rootroot00000000000000 sfwbar-1.0~beta13/icons/weather/heavysleet.svg000066400000000000000000000046751450657570000215250ustar00rootroot00000000000000 sfwbar-1.0~beta13/icons/weather/heavysleetandthunder.svg000066400000000000000000000061051450657570000235700ustar00rootroot00000000000000 sfwbar-1.0~beta13/icons/weather/heavysleetshowers_day.svg000066400000000000000000000100251450657570000237570ustar00rootroot00000000000000 sfwbar-1.0~beta13/icons/weather/heavysleetshowers_night.svg000066400000000000000000000066261450657570000243270ustar00rootroot00000000000000 sfwbar-1.0~beta13/icons/weather/heavysleetshowers_polartwilight.svg000066400000000000000000000110011450657570000260660ustar00rootroot00000000000000 sfwbar-1.0~beta13/icons/weather/heavysleetshowersandthunder_day.svg000066400000000000000000000112131450657570000260340ustar00rootroot00000000000000 sfwbar-1.0~beta13/icons/weather/heavysleetshowersandthunder_night.svg000066400000000000000000000100141450657570000263660ustar00rootroot00000000000000 sfwbar-1.0~beta13/icons/weather/heavysleetshowersandthunder_polartwilight.svg000066400000000000000000000121671450657570000301610ustar00rootroot00000000000000 sfwbar-1.0~beta13/icons/weather/heavysnow.svg000066400000000000000000000044351450657570000213710ustar00rootroot00000000000000 sfwbar-1.0~beta13/icons/weather/heavysnowandthunder.svg000066400000000000000000000056451450657570000234520ustar00rootroot00000000000000 sfwbar-1.0~beta13/icons/weather/heavysnowshowers_day.svg000066400000000000000000000075651450657570000236500ustar00rootroot00000000000000 sfwbar-1.0~beta13/icons/weather/heavysnowshowers_night.svg000066400000000000000000000063661450657570000242020ustar00rootroot00000000000000 sfwbar-1.0~beta13/icons/weather/heavysnowshowers_polartwilight.svg000066400000000000000000000105411450657570000257500ustar00rootroot00000000000000 sfwbar-1.0~beta13/icons/weather/heavysnowshowersandthunder_day.svg000066400000000000000000000107531450657570000257160ustar00rootroot00000000000000 sfwbar-1.0~beta13/icons/weather/heavysnowshowersandthunder_night.svg000066400000000000000000000075541450657570000262570ustar00rootroot00000000000000 sfwbar-1.0~beta13/icons/weather/heavysnowshowersandthunder_polartwilight.svg000066400000000000000000000117271450657570000300340ustar00rootroot00000000000000 sfwbar-1.0~beta13/icons/weather/lightrain.svg000066400000000000000000000033521450657570000213240ustar00rootroot00000000000000 sfwbar-1.0~beta13/icons/weather/lightrainandthunder.svg000066400000000000000000000045621450657570000234050ustar00rootroot00000000000000 sfwbar-1.0~beta13/icons/weather/lightrainshowers_day.svg000066400000000000000000000065021450657570000235740ustar00rootroot00000000000000 sfwbar-1.0~beta13/icons/weather/lightrainshowers_night.svg000066400000000000000000000053031450657570000241260ustar00rootroot00000000000000 sfwbar-1.0~beta13/icons/weather/lightrainshowers_polartwilight.svg000066400000000000000000000074561450657570000257210ustar00rootroot00000000000000 sfwbar-1.0~beta13/icons/weather/lightrainshowersandthunder_day.svg000066400000000000000000000076701450657570000256600ustar00rootroot00000000000000 sfwbar-1.0~beta13/icons/weather/lightrainshowersandthunder_night.svg000066400000000000000000000064711450657570000262120ustar00rootroot00000000000000 sfwbar-1.0~beta13/icons/weather/lightrainshowersandthunder_polartwilight.svg000066400000000000000000000106441450657570000277670ustar00rootroot00000000000000 sfwbar-1.0~beta13/icons/weather/lightsleet.svg000066400000000000000000000041351450657570000215070ustar00rootroot00000000000000 sfwbar-1.0~beta13/icons/weather/lightsleetandthunder.svg000066400000000000000000000053451450657570000235700ustar00rootroot00000000000000 sfwbar-1.0~beta13/icons/weather/lightsleetshowers_day.svg000066400000000000000000000072651450657570000237660ustar00rootroot00000000000000 sfwbar-1.0~beta13/icons/weather/lightsleetshowers_night.svg000066400000000000000000000060661450657570000243200ustar00rootroot00000000000000 sfwbar-1.0~beta13/icons/weather/lightsleetshowers_polartwilight.svg000066400000000000000000000102411450657570000260660ustar00rootroot00000000000000 sfwbar-1.0~beta13/icons/weather/lightsnow.svg000066400000000000000000000036731450657570000213670ustar00rootroot00000000000000 sfwbar-1.0~beta13/icons/weather/lightsnowandthunder.svg000066400000000000000000000051031450657570000234320ustar00rootroot00000000000000 sfwbar-1.0~beta13/icons/weather/lightsnowshowers_day.svg000066400000000000000000000070231450657570000236300ustar00rootroot00000000000000 sfwbar-1.0~beta13/icons/weather/lightsnowshowers_night.svg000066400000000000000000000056241450657570000241710ustar00rootroot00000000000000 sfwbar-1.0~beta13/icons/weather/lightsnowshowers_polartwilight.svg000066400000000000000000000077771450657570000257640ustar00rootroot00000000000000 sfwbar-1.0~beta13/icons/weather/lightssleetshowersandthunder_day.svg000066400000000000000000000104531450657570000262170ustar00rootroot00000000000000 sfwbar-1.0~beta13/icons/weather/lightssleetshowersandthunder_night.svg000066400000000000000000000072541450657570000265600ustar00rootroot00000000000000 sfwbar-1.0~beta13/icons/weather/lightssleetshowersandthunder_polartwilight.svg000066400000000000000000000114271450657570000303350ustar00rootroot00000000000000 sfwbar-1.0~beta13/icons/weather/lightssnowshowersandthunder_day.svg000066400000000000000000000102111450657570000260610ustar00rootroot00000000000000 sfwbar-1.0~beta13/icons/weather/lightssnowshowersandthunder_night.svg000066400000000000000000000070121450657570000264220ustar00rootroot00000000000000 sfwbar-1.0~beta13/icons/weather/lightssnowshowersandthunder_polartwilight.svg000066400000000000000000000111651450657570000302060ustar00rootroot00000000000000 sfwbar-1.0~beta13/icons/weather/partlycloudy_day.svg000066400000000000000000000056651450657570000227440ustar00rootroot00000000000000 sfwbar-1.0~beta13/icons/weather/partlycloudy_night.svg000066400000000000000000000044661450657570000232760ustar00rootroot00000000000000 sfwbar-1.0~beta13/icons/weather/partlycloudy_polartwilight.svg000066400000000000000000000066411450657570000250530ustar00rootroot00000000000000 sfwbar-1.0~beta13/icons/weather/rain.svg000066400000000000000000000035371450657570000203010ustar00rootroot00000000000000 sfwbar-1.0~beta13/icons/weather/rainandthunder.svg000066400000000000000000000047471450657570000223620ustar00rootroot00000000000000 sfwbar-1.0~beta13/icons/weather/rainshowers_day.svg000066400000000000000000000066671450657570000225600ustar00rootroot00000000000000 sfwbar-1.0~beta13/icons/weather/rainshowers_night.svg000066400000000000000000000054701450657570000231030ustar00rootroot00000000000000 sfwbar-1.0~beta13/icons/weather/rainshowers_polartwilight.svg000066400000000000000000000076431450657570000246670ustar00rootroot00000000000000 sfwbar-1.0~beta13/icons/weather/rainshowersandthunder_day.svg000066400000000000000000000100551450657570000246170ustar00rootroot00000000000000 sfwbar-1.0~beta13/icons/weather/rainshowersandthunder_night.svg000066400000000000000000000066561450657570000251670ustar00rootroot00000000000000 sfwbar-1.0~beta13/icons/weather/rainshowersandthunder_polartwilight.svg000066400000000000000000000110311450657570000267260ustar00rootroot00000000000000 sfwbar-1.0~beta13/icons/weather/sleet.svg000066400000000000000000000043221450657570000204550ustar00rootroot00000000000000 sfwbar-1.0~beta13/icons/weather/sleetandthunder.svg000066400000000000000000000055321450657570000225360ustar00rootroot00000000000000 sfwbar-1.0~beta13/icons/weather/sleetshowers_day.svg000066400000000000000000000074521450657570000227340ustar00rootroot00000000000000 sfwbar-1.0~beta13/icons/weather/sleetshowers_night.svg000066400000000000000000000062531450657570000232660ustar00rootroot00000000000000 sfwbar-1.0~beta13/icons/weather/sleetshowers_polartwilight.svg000066400000000000000000000104261450657570000250430ustar00rootroot00000000000000 sfwbar-1.0~beta13/icons/weather/sleetshowersandthunder_day.svg000066400000000000000000000106401450657570000250020ustar00rootroot00000000000000 sfwbar-1.0~beta13/icons/weather/sleetshowersandthunder_night.svg000066400000000000000000000074411450657570000253430ustar00rootroot00000000000000 sfwbar-1.0~beta13/icons/weather/sleetshowersandthunder_polartwilight.svg000066400000000000000000000116141450657570000271200ustar00rootroot00000000000000 sfwbar-1.0~beta13/icons/weather/snow.svg000066400000000000000000000040611450657570000203270ustar00rootroot00000000000000 sfwbar-1.0~beta13/icons/weather/snowandthunder.svg000066400000000000000000000052711450657570000224100ustar00rootroot00000000000000 sfwbar-1.0~beta13/icons/weather/snowshowers_day.svg000066400000000000000000000072111450657570000225770ustar00rootroot00000000000000 sfwbar-1.0~beta13/icons/weather/snowshowers_night.svg000066400000000000000000000060121450657570000231310ustar00rootroot00000000000000 sfwbar-1.0~beta13/icons/weather/snowshowers_polartwilight.svg000066400000000000000000000101651450657570000247150ustar00rootroot00000000000000 sfwbar-1.0~beta13/icons/weather/snowshowersandthunder_day.svg000066400000000000000000000103771450657570000246630ustar00rootroot00000000000000 sfwbar-1.0~beta13/icons/weather/snowshowersandthunder_night.svg000066400000000000000000000072001450657570000252060ustar00rootroot00000000000000 sfwbar-1.0~beta13/icons/weather/snowshowersandthunder_polartwilight.svg000066400000000000000000000113531450657570000267720ustar00rootroot00000000000000 sfwbar-1.0~beta13/meson.build000066400000000000000000000142131450657570000162100ustar00rootroot00000000000000project('sfwbar','c') gtk3 = dependency('gtk+-3.0') glib = dependency('glib-2.0') ggio = dependency('gio-2.0') gio_unix = dependency('gio-unix-2.0') gmod = dependency('gmodule-2.0') json = dependency('json-c') glsh = dependency('gtk-layer-shell-0') wayl = dependency('wayland-client') wayp = dependency('wayland-protocols', version: '>=1.17') puls = dependency('libpulse', required: get_option('pulse')) pulg = dependency('libpulse-mainloop-glib', required: get_option('pulse')) mpdc = dependency('libmpdclient', required: get_option('mpd')) xkbr = dependency('xkbregistry', required: get_option('xkb')) alsa = dependency('alsa', required: get_option('alsa')) conf_data = configuration_data() glsh_ver = glsh.version().split('.') conf_data.set('glsh_major',glsh_ver[0]) conf_data.set('glsh_minor',glsh_ver[1]) conf_data.set('glsh_micro',glsh_ver[2]) conf_data.set('module_dir', get_option('prefix') / get_option('libdir') / 'sfwbar') configure_file(input: 'meson.h.meson', output: 'meson.h', configuration: conf_data ) wayland_scanner = find_program('wayland-scanner') wayland_protodir = wayp.get_pkgconfig_variable('pkgdatadir') wayland_protos = [ 'protocols/wlr-foreign-toplevel-management-unstable-v1', 'protocols/xdg-output-unstable-v1', 'protocols/wlr-layer-shell-unstable-v1' ] wayland_targets=[] foreach proto : wayland_protos xml = ''.join([proto,'.xml']) header = ''.join([proto.split('/').get(-1),'.h']) cfile = ''.join([proto.split('/').get(-1),'.c']) wayland_targets += custom_target(header,output:header,input:xml, command: [ wayland_scanner, 'client-header', '@INPUT@', '@OUTPUT@' ] ) wayland_targets += custom_target(cfile,output:cfile,input:xml, command: [ wayland_scanner, 'public-code', '@INPUT@', '@OUTPUT@' ] ) endforeach src = ['src/sfwbar.c', 'src/bar.c', 'src/basewidget.c', 'src/button.c', 'src/css.c', 'src/label.c', 'src/scale.c', 'src/cchart.c', 'src/image.c', 'src/grid.c', 'src/client.c', 'src/chart.c', 'src/flowitem.c', 'src/hypr_ipc.c', 'src/taskbaritem.c', 'src/menu.c', 'src/sway_ipc.c', 'src/taskbar.c', 'src/taskbargroup.c', 'src/switcher.c', 'src/switcheritem.c', 'src/scanner.c', 'src/wintree.c', 'src/expr.c', 'src/exprlib.c', 'src/jpath.c', 'src/misc.c', 'src/module.c', 'src/mpd.c', 'src/pager.c', 'src/pageritem.c', 'src/popup.c', 'src/scaleimage.c', 'src/flowgrid.c', 'src/wayland.c', 'src/sni.c', 'src/sniitem.c', 'src/snimenu.c', 'src/trayitem.c', 'src/tray.c', 'src/action.c', 'src/actionlib.c', 'src/window.c', 'src/config/base.c', 'src/config/init.c', 'src/config/layout.c', 'src/config/placer.c', 'src/config/scanner.c', 'src/config/switcher.c', 'src/config/toplevel.c', 'src/wayland/foreign-toplevel.c', 'src/wayland/probe.c', 'src/wayland/xdg-output.c', wayland_targets ] dep = [gtk3, glib, gio_unix, gmod, glsh, wayl, json ] man_pages = [ 'doc/sfwbar.1' ] if get_option('network').enabled() or get_option('network').auto() library('network', sources: 'modules/network.c', dependencies: glib, name_prefix: '', install: true, install_dir: get_option('libdir') / 'sfwbar' ) man_pages += 'doc/sfwbar-network.1' endif if get_option('bluez').enabled() or get_option('bluez').auto() library('bluez', sources: 'modules/bluez.c', dependencies: [glib, ggio], name_prefix: '', install: true, install_dir: get_option('libdir') / 'sfwbar' ) man_pages += 'doc/sfwbar-bluez.1' endif if puls.found() and pulg.found() library('pulsectl', sources: 'modules/pulsectl.c', dependencies: [glib, puls, pulg],name_prefix: '', install: true, install_dir: get_option('libdir') / 'sfwbar' ) man_pages += 'doc/sfwbar-pulse.1' endif if mpdc.found() library('mpd', sources: 'modules/mpd.c', dependencies: [glib, mpdc], name_prefix: '', install: true, install_dir: get_option('libdir') / 'sfwbar' ) man_pages += 'doc/sfwbar-mpd.1' endif if alsa.found() library('alsactl', sources: 'modules/alsactl.c', dependencies: [glib, alsa], name_prefix: '', install: true, install_dir: get_option('libdir') / 'sfwbar' ) man_pages += 'doc/sfwbar-alsa.1' endif if xkbr.found() library('xkbmap', sources: 'modules/xkbmap.c', dependencies: [glib, xkbr], name_prefix: '', install: true, install_dir: get_option('libdir') / 'sfwbar' ) man_pages += 'doc/sfwbar-xkbmap.1' endif if get_option('bsdctl').enabled() library('bsdctl', sources: 'modules/bsdctl.c', dependencies: glib, name_prefix: '', install: true, install_dir: get_option('libdir') / 'sfwbar' ) man_pages += 'doc/sfwbar-bsdctl.1' endif if get_option('idleinhibit').enabled() or get_option('idleinhibit').auto() inhibit_targets = [ custom_target('idle-inhibit-unstable-v1.h', output:'idle-inhibit-unstable-v1.h', input: wayland_protodir / 'unstable/idle-inhibit/idle-inhibit-unstable-v1.xml', command: [ wayland_scanner, 'client-header', '@INPUT@', '@OUTPUT@' ] ), custom_target('idle-inhibit-unstable-v1.c', output:'idle-inhibit-unstable-v1.c', input: wayland_protodir / 'unstable/idle-inhibit/idle-inhibit-unstable-v1.xml', command: [ wayland_scanner, 'public-code', '@INPUT@', '@OUTPUT@' ] ) ] library('idleinhibit', sources: ['modules/idleinhibit.c',inhibit_targets], dependencies: [glib, gtk3, wayl], name_prefix: '', install: true, install_dir: get_option('libdir') / 'sfwbar' ) man_pages += 'doc/sfwbar-idleinhibit.1' endif executable ('sfwbar', sources: src, c_args: ['-DGLIB_DISABLE_DEPRECATION_WARNINGS', '-DGDK_DISABLE_DEPRECATED', '-DGTK_DISABLE_DEPRECATED'], dependencies: dep, install: true) foreach page : man_pages install_man(page) endforeach install_subdir('config', install_dir: get_option('datadir') / 'sfwbar', strip_directory : true ) install_subdir('icons/weather', install_dir: get_option('datadir') / 'sfwbar/icons' ) install_subdir('icons/misc', install_dir: get_option('datadir') / 'sfwbar/icons' ) install_data('icons/sfwbar.svg', install_dir: get_option('datadir') / 'icons/hicolor/scalable/apps' ) sfwbar-1.0~beta13/meson.h.meson000066400000000000000000000002361450657570000164600ustar00rootroot00000000000000 #define GTK_LAYER_VER_MAJOR @glsh_major@ #define GTK_LAYER_VER_MINOR @glsh_minor@ #define GTK_LAYER_VER_MICRO @glsh_micro@ #define MODULE_DIR "@module_dir@" sfwbar-1.0~beta13/meson_options.txt000066400000000000000000000011661450657570000175060ustar00rootroot00000000000000option('alsa',type:'feature',value:'auto',description:'ALSA module') option('bluez',type:'feature',value:'auto',description:'Bluez module') option('bsdctl',type:'feature',value:'disabled',description:'BSD sysctl module') option('idleinhibit',type:'feature',value:'auto',description:'Idle inhibit protocol support') option('network',type:'feature',value:'auto',description:'Network module') option('pulse',type:'feature',value:'auto',description:'Pulse Audio module') option('mpd',type:'feature',value:'auto',description:'Music Player Daemon module') option('xkb',type:'feature',value:'auto',description:'xkbcommon layout lookup') sfwbar-1.0~beta13/modules/000077500000000000000000000000001450657570000155155ustar00rootroot00000000000000sfwbar-1.0~beta13/modules/alsactl.c000066400000000000000000000175331450657570000173150ustar00rootroot00000000000000/* This entire file is licensed under GNU General Public License v3.0 * * Copyright 2023- Sfwbar maintainers */ #include #include #include "../src/module.h" ModuleApiV1 *sfwbar_module_api; gint64 sfwbar_module_signature = 0x73f4d956a1; guint16 sfwbar_module_version = 1; static GSource *main_src; static snd_mixer_t *mixer; static struct pollfd *pfds; static int pfdcount; typedef struct _mixer_api { int (*has_volume)( snd_mixer_elem_t *); int (*has_channel)( snd_mixer_elem_t *, snd_mixer_selem_channel_id_t ); int (*get_range) ( snd_mixer_elem_t *, long *, long *); int (*get_channel)(snd_mixer_elem_t *, snd_mixer_selem_channel_id_t, long *); int (*set_channel)(snd_mixer_elem_t *, snd_mixer_selem_channel_id_t, long ); int (*has_switch)( snd_mixer_elem_t *); int (*get_switch)( snd_mixer_elem_t *, snd_mixer_selem_channel_id_t, int *); int (*set_switch) ( snd_mixer_elem_t *, int ); } mixer_api_t; static mixer_api_t playback_api = { .has_volume = snd_mixer_selem_has_playback_volume, .has_channel = snd_mixer_selem_has_playback_channel, .get_range = snd_mixer_selem_get_playback_volume_range, .get_channel = snd_mixer_selem_get_playback_volume, .set_channel = snd_mixer_selem_set_playback_volume, .has_switch = snd_mixer_selem_has_playback_switch, .get_switch = snd_mixer_selem_get_playback_switch, .set_switch = snd_mixer_selem_set_playback_switch_all }; static mixer_api_t capture_api = { .has_volume = snd_mixer_selem_has_capture_volume, .has_channel = snd_mixer_selem_has_capture_channel, .get_range = snd_mixer_selem_get_capture_volume_range, .get_channel = snd_mixer_selem_get_capture_volume, .set_channel = snd_mixer_selem_set_capture_volume, .has_switch = snd_mixer_selem_has_capture_switch, .get_switch = snd_mixer_selem_get_capture_switch, .set_switch = snd_mixer_selem_set_capture_switch_all }; gboolean alsa_source_prepare(GSource *source, gint *timeout) { *timeout = -1; return FALSE; } gboolean alsa_source_check( GSource *source ) { gushort revents; snd_mixer_poll_descriptors_revents(mixer, pfds, pfdcount, &revents); return !!(revents & POLLIN); } gboolean alsa_source_dispatch( GSource *source,GSourceFunc cb, gpointer data) { snd_mixer_handle_events(mixer); MODULE_TRIGGER_EMIT("alsa"); return TRUE; } static void alsa_source_finalize ( GSource *source ) { g_clear_pointer(&mixer,snd_mixer_close); g_clear_pointer(&pfds,g_free); } static GSourceFuncs alsa_source_funcs = { alsa_source_prepare, alsa_source_check, alsa_source_dispatch, alsa_source_finalize }; #define alsa_source_bail(x) { g_source_destroy(x); return NULL; } static GSource *alsa_source_subscribe ( gchar *name ) { GSource *source; source = g_source_new(&alsa_source_funcs, sizeof(GSource)); if(snd_mixer_open(&mixer, 0) < 0) alsa_source_bail(source); if(snd_mixer_attach(mixer, name) < 0) alsa_source_bail(source); if(snd_mixer_selem_register(mixer, NULL, NULL) < 0) alsa_source_bail(source); if(snd_mixer_load(mixer) < 0) alsa_source_bail(source); pfdcount = snd_mixer_poll_descriptors_count (mixer); if(pfdcount <= 0) alsa_source_bail(source); pfds = g_malloc(sizeof(struct pollfd) * pfdcount); if(snd_mixer_poll_descriptors(mixer, pfds, pfdcount) < 0) alsa_source_bail(source); g_source_attach(source,NULL); g_source_set_priority(source,G_PRIORITY_DEFAULT); g_source_add_poll(source,(GPollFD *)pfds); return source; } void sfwbar_module_init ( ModuleApiV1 *api ) { sfwbar_module_api = api; main_src = alsa_source_subscribe("default"); } static snd_mixer_elem_t *alsa_element_get ( gchar *name ) { snd_mixer_selem_id_t *sid; snd_mixer_selem_id_alloca(&sid); snd_mixer_selem_id_set_index(sid, 0); snd_mixer_selem_id_set_name(sid, name?name:"Master"); return snd_mixer_find_selem(mixer, sid); } static glong alsa_volume_avg_get ( snd_mixer_elem_t *element, mixer_api_t *api ) { glong vol, tvol = 0; gint i, count = 0; for(i=0;i<=SND_MIXER_SCHN_LAST;i++) if(api->has_channel(element, i)) { api->get_channel(element, i, &vol); tvol += vol; count++; } return tvol/count; } static gdouble alsa_volume_get ( snd_mixer_elem_t *element, mixer_api_t *api ) { glong min, max, vol; if(!api->has_volume(element)) return 0; api->get_range(element, &min, &max); vol = alsa_volume_avg_get(element, api ); return ((gdouble)vol-min)/(max-min)*100; } static gdouble alsa_mute_get ( snd_mixer_elem_t *element, mixer_api_t *api ) { gint pb; if(!api->has_switch(element)) return FALSE; api->get_switch(element, 0, &pb); return !pb; } void *alsa_expr_func ( void **params, void *widget, void *event ) { snd_mixer_elem_t *element; gdouble *result; result = g_malloc0(sizeof(gdouble)); if(!params || !params[0]) return result; element = alsa_element_get(params[1]); if(!g_ascii_strcasecmp(params[0],"playback-volume")) *result = alsa_volume_get(element, &playback_api); else if(!g_ascii_strcasecmp(params[0],"capture-volume")) *result = alsa_volume_get(element, &capture_api); else if(!g_ascii_strcasecmp(params[0],"playback-mute")) *result = alsa_mute_get(element, &playback_api); else if(!g_ascii_strcasecmp(params[0],"capture-mute")) *result = alsa_mute_get(element, &capture_api); return result; } static void alsa_volume_adjust ( snd_mixer_elem_t *element, gchar *vstr, mixer_api_t *api ) { long min, max, vol, vdelta; gint i; if(!api->has_volume(element)) return; api->get_range(element, &min, &max); vol = alsa_volume_avg_get(element, api); while(*vstr==' ') vstr++; vdelta = g_ascii_strtod(vstr,NULL); vdelta = (vdelta*(max-min) + ((vdelta<0)?-50:50))/100; switch(*vstr) { case '+': vdelta = MAX(1,vdelta); break; case '-': vdelta = MIN(-1,vdelta); break; default: vdelta = vdelta - vol; break; } for(i=0;i<=SND_MIXER_SCHN_LAST;i++) if(api->has_channel(element, i)) { api->get_channel(element, i, &vol); api->set_channel(element, i, CLAMP(vol + vdelta,min,max)); } } static void alsa_mute_set ( snd_mixer_elem_t *element, gchar *vstr, mixer_api_t *api ) { gint pb; if(!api->has_switch(element)) return; while(*vstr==' ') vstr++; if(!g_ascii_strcasecmp(vstr,"on")) api->set_switch(element,0); else if(!g_ascii_strcasecmp(vstr,"off")) api->set_switch(element,1); else if(!g_ascii_strcasecmp(vstr,"toggle")) { api->get_switch(element, 0, &pb); api->set_switch(element, !pb); } } static void alsa_action ( gchar *cmd, gchar *name, void *d1, void *d2, void *d3, void *d4 ) { snd_mixer_elem_t *element; element = alsa_element_get ( name ); if(!g_ascii_strncasecmp(cmd,"playback-volume",15)) alsa_volume_adjust(element, cmd+15, &playback_api); else if(!g_ascii_strncasecmp(cmd,"playback-mute",13)) alsa_mute_set(element, cmd+13, &playback_api); else if(!g_ascii_strncasecmp(cmd,"capture-volume",14)) alsa_volume_adjust(element, cmd+14, &capture_api); else if(!g_ascii_strncasecmp(cmd,"capture-mute",12)) alsa_mute_set(element, cmd+12, &capture_api); else return; MODULE_TRIGGER_EMIT("alsa"); } static void alsa_card_action ( gchar *cmd, gchar *name, void *d1, void *d2, void *d3, void *d4 ) { if(!cmd) return; g_clear_pointer((GSource **)&main_src,g_source_destroy); main_src = alsa_source_subscribe(cmd); } ModuleExpressionHandlerV1 handler1 = { .flags = MODULE_EXPR_NUMERIC, .name = "Alsa", .parameters = "Ss", .function = alsa_expr_func }; ModuleExpressionHandlerV1 *sfwbar_expression_handlers[] = { &handler1, NULL }; ModuleActionHandlerV1 act_handler1 = { .name = "AlsaCmd", .function = alsa_action }; ModuleActionHandlerV1 act_handler2 = { .name = "AlsaSetCard", .function = alsa_card_action }; ModuleActionHandlerV1 *sfwbar_action_handlers[] = { &act_handler1, &act_handler2, NULL }; sfwbar-1.0~beta13/modules/bluez.c000066400000000000000000000471331450657570000170120ustar00rootroot00000000000000/* This entire file is licensed under GNU General Public License v3.0 * * Copyright 2023- Sfwbar maintainers */ #include #include #include "../src/module.h" ModuleApiV1 *sfwbar_module_api; gint64 sfwbar_module_signature = 0x73f4d956a1; guint16 sfwbar_module_version = 1; static GHashTable *devices; static GList *adapters, *remove_queue, *update_queue; static GMutex adapter_mutex, remove_mutex, update_mutex; static GDBusConnection *bz_con; static const gchar *bz_serv = "org.bluez"; typedef struct _bz_device { gchar *path; gchar *addr; gchar *name; gchar *icon; gboolean paired; gboolean trusted; gboolean connected; gboolean connecting; } BzDevice; typedef struct _bz_adapter { gchar *path; gchar *iface; guint scan_timeout; guint timeout_handle; } BzAdapter; static void bz_adapter_free ( gchar *object ) { GList *iter; BzAdapter *adapter; for(iter=adapters; iter; iter=g_list_next(iter)) if(!g_strcmp0(((BzAdapter *)(iter->data))->path, object)) break; if(!iter) return; adapter = iter->data; g_mutex_lock(&adapter_mutex); adapters = g_list_remove(adapters, adapter); g_mutex_unlock(&adapter_mutex); if(!adapters) MODULE_TRIGGER_EMIT("bluez_running"); if(adapter->timeout_handle) g_source_remove(adapter->timeout_handle); g_free(adapter->path); g_free(adapter->iface); g_free(adapter); } static void bz_device_free ( BzDevice *device ) { g_free(device->path); g_free(device->addr); g_free(device->name); g_free(device->icon); g_free(device); } static BzDevice *bz_device_dup ( BzDevice *src ) { BzDevice *dest; dest = g_malloc0(sizeof(BzDevice)); dest->path = g_strdup(src->path); dest->addr = g_strdup(src->addr); dest->name = g_strdup(src->name); dest->icon = g_strdup(src->icon); dest->paired = src->paired; dest->trusted = src->trusted; dest->connected = src->connected; dest->connecting = src->connecting; return dest; } static void bz_device_update ( BzDevice *device ) { g_mutex_lock(&update_mutex); update_queue = g_list_append(update_queue, bz_device_dup(device)); g_mutex_unlock(&update_mutex); if(!g_list_next(update_queue)) MODULE_TRIGGER_EMIT("bluez_updated"); } static BzAdapter *bz_adapter_get ( void ) { if(!adapters) return NULL; return adapters->data; } static void bz_scan_stop_cb ( GDBusConnection *con, GAsyncResult *res, BzAdapter *adapter) { GVariant *result; MODULE_TRIGGER_EMIT("bluez_scan_complete"); result = g_dbus_connection_call_finish(con, res, NULL); if(result) g_variant_unref(result); } static gboolean bz_scan_stop ( BzAdapter *adapter ) { g_debug("bluez: scan off"); g_dbus_connection_call(bz_con, bz_serv, adapter->path, adapter->iface, "StopDiscovery", NULL, NULL, G_DBUS_CALL_FLAGS_NONE, -1, NULL, (GAsyncReadyCallback)bz_scan_stop_cb, NULL); adapter->timeout_handle = 0; return FALSE; } static void bz_scan_cb ( GDBusConnection *con, GAsyncResult *res, BzAdapter *adapter) { GVariant *result; result = g_dbus_connection_call_finish(con, res, NULL); if(!result) { MODULE_TRIGGER_EMIT("bluez_scan_complete"); return; } g_variant_unref(result); if(adapter->scan_timeout) adapter->timeout_handle = g_timeout_add(adapter->scan_timeout, (GSourceFunc)bz_scan_stop, adapter); } static void bz_scan_filter_cb ( GDBusConnection *con, GAsyncResult *res, BzAdapter *adapter) { GVariant *result; result = g_dbus_connection_call_finish(con, res, NULL); if(!result) { MODULE_TRIGGER_EMIT("bluez_scan_complete"); return; } g_debug("bluez: scan on"); g_dbus_connection_call(con, bz_serv, adapter->path, adapter->iface, "StartDiscovery", NULL, NULL, G_DBUS_CALL_FLAGS_NONE, -1, NULL, (GAsyncReadyCallback)bz_scan_cb, adapter); g_variant_unref(result); } static void bz_scan ( GDBusConnection *con, guint timeout ) { BzAdapter *adapter; GVariantBuilder *builder; GVariant *dict; adapter = bz_adapter_get(); if(!adapter || adapter->timeout_handle) return; MODULE_TRIGGER_EMIT("bluez_scan"); builder = g_variant_builder_new(G_VARIANT_TYPE_VARDICT); g_variant_builder_add(builder, "{sv}", "Transport", g_variant_new_string("bredr")); dict = g_variant_builder_end(builder); g_variant_builder_unref(builder); adapter->scan_timeout = timeout; g_dbus_connection_call(con, bz_serv, adapter->path, adapter->iface, "SetDiscoveryFilter", g_variant_new_tuple(&dict,1), NULL, G_DBUS_CALL_FLAGS_NONE, -1, NULL, (GAsyncReadyCallback)bz_scan_filter_cb, adapter); } static void bz_connect_cb ( GDBusConnection *con, GAsyncResult *res, BzDevice *device) { GVariant *result; device->connecting = FALSE; bz_device_update(device); result = g_dbus_connection_call_finish(con, res, NULL); if(!result) return; g_debug("bluez: connected %s (%s)", device->addr, device->name); g_variant_unref(result); } static void bz_connect ( BzDevice *device ) { if(!device->connecting) { device->connecting = TRUE; bz_device_update(device); } g_debug("bluez: attempting to connect %s (%s)", device->addr, device->name); g_dbus_connection_call(bz_con, bz_serv, device->path, "org.bluez.Device1", "Connect", NULL, NULL, G_DBUS_CALL_FLAGS_NONE, -1, NULL, (GAsyncReadyCallback)bz_connect_cb, device); } static void bz_disconnect_cb ( GDBusConnection *con, GAsyncResult *res, BzDevice *device) { GVariant *result; result = g_dbus_connection_call_finish(con, res, NULL); if(!result) return; g_debug("bluez: disconnected %s (%s)", device->addr, device->name); g_variant_unref(result); } static void bz_disconnect ( BzDevice *device ) { if(!device->connected) return; g_debug("bluez: attempting to disconnect %s (%s)", device->addr, device->name); g_dbus_connection_call(bz_con, bz_serv, device->path, "org.bluez.Device1", "Disconnect", NULL, NULL, G_DBUS_CALL_FLAGS_NONE, -1, NULL, (GAsyncReadyCallback)bz_disconnect_cb, device); } static void bz_trust_cb ( GDBusConnection *con, GAsyncResult *res, BzDevice *device) { GVariant *result; result = g_dbus_connection_call_finish(con, res, NULL); if(!result) { device->connecting = FALSE; bz_device_update(device); return; } g_debug("bluez: trusted %s (%s)", device->addr, device->name); bz_connect(device); g_variant_unref(result); } static void bz_trust ( BzDevice *device ) { if(device->trusted) { bz_connect(device); return; } g_debug("bluez: attempting to trust %s (%s)", device->addr, device->name); g_dbus_connection_call(bz_con, bz_serv, device->path, "org.freedesktop.DBus.Properties", "Set", g_variant_new("(ssv)", "org.bluez.Device1","Trusted",g_variant_new_boolean(TRUE)), NULL, G_DBUS_CALL_FLAGS_NONE, -1, NULL, (GAsyncReadyCallback)bz_trust_cb, device); } static void bz_pair_cb ( GDBusConnection *con, GAsyncResult *res, BzDevice *device) { GVariant *result; result = g_dbus_connection_call_finish(con, res, NULL); if(!result) { device->connecting = FALSE; bz_device_update(device); return; } g_debug("bluez: paired %s (%s)", device->addr, device->name); bz_trust(device); g_variant_unref(result); } static void bz_pair ( BzDevice *device ) { device->connecting = TRUE; bz_device_update(device); if(device->paired) { bz_trust(device); return; } g_debug("bluez: attempting to pair %s (%s)", device->addr, device->name); g_dbus_connection_call(bz_con, bz_serv, device->path, "org.bluez.Device1", "Pair", NULL, NULL, G_DBUS_CALL_FLAGS_NONE, -1, NULL, (GAsyncReadyCallback)bz_pair_cb, device); } static void bz_remove_cb ( GDBusConnection *con, GAsyncResult *res, gchar *name) { GVariant *result; result = g_dbus_connection_call_finish(con, res, NULL); if(result) { g_debug("bluez: removed %s", name); g_variant_unref(result); } g_free(name); } static void bz_remove ( BzDevice *device ) { BzAdapter *adapter; adapter = bz_adapter_get(); if(!adapter) return; g_debug("bluez: attempting to remove %s (%s)", device->addr, device->name); g_dbus_connection_call(bz_con, bz_serv, adapter->path, "org.bluez.Adapter1", "RemoveDevice", g_variant_new("(o)",device->path), NULL, G_DBUS_CALL_FLAGS_NONE, -1, NULL, (GAsyncReadyCallback)bz_remove_cb, g_strdup(device->name)); g_dbus_connection_call(bz_con, bz_serv, device->path, "org.freedesktop.DBus.Properties", "Set", g_variant_new("(ssv)", "org.bluez.Device1","Trusted",g_variant_new_boolean(FALSE)), NULL, G_DBUS_CALL_FLAGS_NONE, -1, NULL, NULL, NULL); } static gboolean bz_device_property_string ( gchar **prop, GVariant *val ) { gchar *result; g_variant_get(val, "s", &result); if(!g_strcmp0(*prop, result)) { g_free(result); return FALSE; } g_clear_pointer(prop, g_free); *prop = result; return TRUE; } static gboolean bz_device_properties ( BzDevice *device, GVariantIter *piter ) { gboolean changed = FALSE; gchar *prop; GVariant *val; while(g_variant_iter_next(piter, "{&sv}", &prop, &val)) { if(!g_strcmp0(prop, "Name")) changed |= bz_device_property_string(&device->name, val); else if(!g_strcmp0(prop, "Icon")) changed |= bz_device_property_string(&device->icon, val); else if(!g_strcmp0(prop, "Address")) changed |= bz_device_property_string(&device->addr, val); else if(!g_strcmp0(prop, "Paired") && device->paired != g_variant_get_boolean(val)) { device->paired = g_variant_get_boolean(val); changed = TRUE; } else if(!g_strcmp0(prop, "Trusted") && device->trusted != g_variant_get_boolean(val)) { device->trusted = g_variant_get_boolean(val); changed = TRUE; } else if(!g_strcmp0(prop, "Connected") && device->connected != g_variant_get_boolean(val)) { device->connected = g_variant_get_boolean(val); changed = TRUE; } g_variant_unref(val); } return changed; } static void bz_device_handle ( gchar *path, gchar *iface, GVariantIter *piter ) { BzDevice *device; device = devices?g_hash_table_lookup(devices, path):NULL; if(!device) { device = g_malloc0(sizeof(BzDevice)); device->path = g_strdup(path); if(!devices) devices = g_hash_table_new_full(g_str_hash, g_str_equal, NULL, (GDestroyNotify)bz_device_free); g_hash_table_insert(devices, device->path, device); } bz_device_properties (device, piter); bz_device_update(device); g_debug("bluez: device added: %d %d %s %s on %s",device->paired, device->connected, device->addr, device->name, device->path); } static void bz_adapter_handle ( gchar *object, gchar *iface ) { BzAdapter *adapter; GList *iter; for(iter=adapters; iter; iter=g_list_next(iter)) if(!g_strcmp0(((BzAdapter *)(iter->data))->path, object)) return; adapter = g_malloc0(sizeof(BzAdapter)); adapter->path = g_strdup(object); adapter->iface = g_strdup(iface); g_mutex_lock(&adapter_mutex); adapters = g_list_append(adapters, adapter); g_mutex_unlock(&adapter_mutex); if(adapters && !g_list_next(adapters)) MODULE_TRIGGER_EMIT("bluez_running"); } static void bz_object_handle ( gchar *object, GVariantIter *iiter ) { GVariantIter *dict; gchar *iface; while(g_variant_iter_next(iiter, "{&sa{sv}}", &iface, &dict)) { if(strstr(iface,"Device")) bz_device_handle(object, iface, dict); else if(strstr(iface,"Adapter")) bz_adapter_handle(object, iface); g_variant_iter_free(dict); } g_variant_iter_free(iiter); } static void bz_device_new ( GDBusConnection *con, const gchar *sender, const gchar *path, const gchar *iface, const gchar *signal, GVariant *params, gpointer data ) { GVariantIter *iiter; gchar *object; g_variant_get(params, "(&oa{sa{sv}})", &object, &iiter); bz_object_handle(object, iiter); } static void bz_device_removed ( GDBusConnection *con, const gchar *sender, const gchar *path, const gchar *iface, const gchar *signal, GVariant *params, gpointer data ) { gchar *object; BzDevice *device; g_variant_get(params, "(&o@as)", &object, NULL); bz_adapter_free(object); device = g_hash_table_lookup(devices, object); if(device) { g_mutex_lock(&remove_mutex); remove_queue = g_list_append(remove_queue, g_strdup(device->path)); g_mutex_unlock(&remove_mutex); g_debug("bluez: device removed: %d %d %s %s on %s",device->paired, device->connected, device->addr, device->name, device->path); g_hash_table_remove(devices, object); if(!remove_queue->next) MODULE_TRIGGER_EMIT("bluez_removed"); } } static void bz_device_changed ( GDBusConnection *con, const gchar *sender, const gchar *path, const gchar *iface, const gchar *signal, GVariant *params, gpointer data ) { BzDevice *device; GVariantIter *piter; if(!devices) return; device = g_hash_table_lookup(devices, path); if(!device) return; g_variant_get(params,"(&sa{sv}@as)", NULL, &piter, NULL); if(bz_device_properties(device, piter)) { g_debug("bluez: device changed: %d %d %s %s on %s",device->paired, device->connected, device->addr, device->name, device->path); bz_device_update(device); } g_variant_iter_free(piter); } static void bz_init_cb ( GDBusConnection *con, GAsyncResult *res, gpointer data ) { GVariant *result, *child; GVariantIter miter, *iiter; gchar *path; result = g_dbus_connection_call_finish(con, res, NULL); if(!result) return; child = g_variant_get_child_value(result, 0); g_variant_iter_init(&miter, child); while(g_variant_iter_next(&miter, "{&oa{sa{sv}}}", &path, &iiter)) bz_object_handle(path, iiter); g_variant_unref(child); g_variant_unref(result); g_dbus_connection_signal_subscribe(con, bz_serv, "org.freedesktop.DBus.ObjectManager", "InterfacesAdded", NULL, NULL, G_DBUS_SIGNAL_FLAGS_NONE, bz_device_new, NULL, NULL); g_dbus_connection_signal_subscribe(con, bz_serv, "org.freedesktop.DBus.ObjectManager", "InterfacesRemoved", NULL, NULL, G_DBUS_SIGNAL_FLAGS_NONE, bz_device_removed, NULL, NULL); g_dbus_connection_signal_subscribe(con, bz_serv, "org.freedesktop.DBus.Properties", "PropertiesChanged", NULL, "org.bluez.Device1", G_DBUS_SIGNAL_FLAGS_NONE, bz_device_changed, NULL, NULL); } static void bz_name_appeared_cb (GDBusConnection *con, const gchar *name, const gchar *owner, gpointer d) { g_dbus_connection_call(con, bz_serv,"/", "org.freedesktop.DBus.ObjectManager", "GetManagedObjects", NULL, G_VARIANT_TYPE("(a{oa{sa{sv}}})"), G_DBUS_CALL_FLAGS_NONE, -1, NULL, (GAsyncReadyCallback)bz_init_cb, NULL); } static void bz_name_disappeared_cb (GDBusConnection *con, const gchar *name, gpointer d) { while(adapters) bz_adapter_free(adapters->data); g_hash_table_remove_all(devices); } void sfwbar_module_init ( ModuleApiV1 *api ) { sfwbar_module_api = api; bz_con = g_bus_get_sync(G_BUS_TYPE_SYSTEM, NULL, NULL); g_bus_watch_name(G_BUS_TYPE_SYSTEM, bz_serv, G_BUS_NAME_WATCHER_FLAGS_NONE, bz_name_appeared_cb, bz_name_disappeared_cb, NULL, NULL); } static void *bz_expr_get ( void **params, void *widget, void *event ) { BzDevice *device; if(!params || !params[0]) return g_strdup(""); if(update_queue) { device = update_queue->data; if(!g_ascii_strcasecmp(params[0],"Name")) return g_strdup(device->name?device->name:""); if(!g_ascii_strcasecmp(params[0],"Address")) return g_strdup(device->addr?device->addr:""); if(!g_ascii_strcasecmp(params[0],"Icon")) return g_strdup(device->icon?device->icon:""); if(!g_ascii_strcasecmp(params[0],"Path")) return g_strdup(device->path?device->path:""); } if(remove_queue && !g_ascii_strcasecmp(params[0],"RemovedPath")) return g_strdup(remove_queue->data); return g_strdup(""); } static void *bz_expr_state ( void **params, void *widget, void *event ) { BzDevice *device; gdouble *result; result = g_malloc0(sizeof(gdouble)); if(!params || !params[0]) return result; if(!g_ascii_strcasecmp(params[0],"Running")) { *result = !!adapters; return result; } if(!update_queue) return result; device = update_queue->data; if(!g_ascii_strcasecmp(params[0],"Paired")) *result = device->paired; else if(!g_ascii_strcasecmp(params[0],"Connected")) *result = device->connected; else if(!g_ascii_strcasecmp(params[0],"Connecting")) *result = device->connecting; else if(!g_ascii_strcasecmp(params[0],"Trusted")) *result = device->trusted; return result; } static void bz_action_ack ( gchar *cmd, gchar *name, void *d1, void *d2, void *d3, void *d4 ) { BzDevice *device; if(!update_queue) return; device = update_queue->data; g_mutex_lock(&update_mutex); update_queue = g_list_remove(update_queue, device); g_mutex_unlock(&update_mutex); bz_device_free(device); g_debug("bluez: ack processed, queue: %d", !!update_queue); if(update_queue) MODULE_TRIGGER_EMIT("bluez_updated"); } static void bz_action_ack_removed ( gchar *cmd, gchar *name, void *d1, void *d2, void *d3, void *d4 ) { gchar *tmp; if(!remove_queue) return; tmp = remove_queue->data; g_mutex_lock(&remove_mutex); remove_queue = g_list_remove(remove_queue, tmp); g_mutex_unlock(&remove_mutex); g_free(tmp); if(remove_queue) MODULE_TRIGGER_EMIT("bluez_removed"); } static void bz_action_scan ( gchar *cmd, gchar *name, void *d1, void *d2, void *d3, void *d4 ) { bz_scan(bz_con, 10000); } static void bz_action_connect ( gchar *cmd, gchar *name, void *d1, void *d2, void *d3, void *d4 ) { BzDevice *device; if(!devices) return; device = g_hash_table_lookup(devices, cmd); if(!device || device->connected) return; bz_connect(device); } static void bz_action_pair ( gchar *cmd, gchar *name, void *d1, void *d2, void *d3, void *d4 ) { BzDevice *device; if(!devices) return; device = g_hash_table_lookup(devices, cmd); if(device) bz_pair(device); } static void bz_action_disconnect ( gchar *cmd, gchar *name, void *d1, void *d2, void *d3, void *d4 ) { BzDevice *device; if(!devices) return; device = g_hash_table_lookup(devices, cmd); if(device) bz_disconnect(device); } static void bz_action_remove ( gchar *cmd, gchar *name, void *d1, void *d2, void *d3, void *d4 ) { BzDevice *device; if(!devices) return; device = g_hash_table_lookup(devices, cmd); if(device) bz_remove(device); } ModuleExpressionHandlerV1 get_handler = { .flags = 0, .name = "BluezGet", .parameters = "S", .function = bz_expr_get }; ModuleExpressionHandlerV1 state_handler = { .flags = MODULE_EXPR_NUMERIC, .name = "BluezState", .parameters = "S", .function = bz_expr_state }; ModuleExpressionHandlerV1 *sfwbar_expression_handlers[] = { &get_handler, &state_handler, NULL }; ModuleActionHandlerV1 ack_handler = { .name = "BluezAck", .function = bz_action_ack }; ModuleActionHandlerV1 ack_removed_handler = { .name = "BluezAckRemoved", .function = bz_action_ack_removed }; ModuleActionHandlerV1 scan_handler = { .name = "BluezScan", .function = bz_action_scan }; ModuleActionHandlerV1 connect_handler = { .name = "BluezConnect", .function = bz_action_connect }; ModuleActionHandlerV1 pair_handler = { .name = "BluezPair", .function = bz_action_pair }; ModuleActionHandlerV1 remove_handler = { .name = "BluezRemove", .function = bz_action_remove }; ModuleActionHandlerV1 disconnect_handler = { .name = "BluezDisconnect", .function = bz_action_disconnect }; ModuleActionHandlerV1 *sfwbar_action_handlers[] = { &ack_handler, &ack_removed_handler, &scan_handler, &connect_handler, &disconnect_handler, &pair_handler, &remove_handler, NULL }; sfwbar-1.0~beta13/modules/bsdctl.c000066400000000000000000000161501450657570000171370ustar00rootroot00000000000000/* This entire file is licensed under GNU General Public License v3.0 * * Copyright 2023- Sfwbar maintainers */ #include "../src/module.h" #include #include #include #include #include #include #include #include #include ModuleApiV1 *sfwbar_module_api; gint64 sfwbar_module_signature = 0x73f4d956a1; guint16 sfwbar_module_version = 1; typedef gchar *(*SysctlParseFunc)( void *, size_t ); typedef struct { gint *oid; gsize len; guint type; SysctlParseFunc func; } SysctlVar; static gchar *sysctl_clockinfo ( void *buf, size_t len ) { struct clockinfo *ci; if(len != sizeof(struct clockinfo)) return g_strdup("clockinfo: invalid data"); ci = buf; return g_strdup_printf("{ hz = %d, tick = %d, profhz = %d, stathz = %d }", ci->hz, ci->tick, ci->profhz, ci->stathz); } static gchar *sysctl_timeval ( void *buf, size_t len ) { struct timeval *tv; if(len != sizeof(struct timeval)) return g_strdup("timeval: invalid data"); tv = buf; return g_strdup_printf("{ sec = %jd, usec = %lu } %s", tv->tv_sec, tv->tv_usec, ctime(&(tv->tv_sec))); } static gchar *sysctl_loadavg ( void *buf, size_t len ) { struct loadavg *la; if(len != sizeof(struct loadavg)) return g_strdup("loadavg: invalid data"); la = buf; return g_strdup_printf("{ %.2f %.2f %.2f }", (double)la->ldavg[0]/(double)la->fscale, (double)la->ldavg[1]/(double)la->fscale, (double)la->ldavg[2]/(double)la->fscale); } static gchar *sysctl_vmtotal ( void *buf, size_t len ) { struct vmtotal *vm; int kpp; if(len != sizeof(struct vmtotal)) return g_strdup("vmtotal: invalid data"); vm = buf; kpp = getpagesize()/1024; return g_strdup_printf("Processes:\t\t" "(RUNQ: %hd Disk Wait: %hd Page Wait: %hd Sleep: %hd)\n" "Virtual Memory:\t\t(Total %ldK Active %ldK)\n" "Real Memory:\t\t(Total %ldK Active %ldK)\n" "Shared Virtual Memory:\t(Total %ldK Active %ldK)\n" "Shared Real Memory:\t(Total %ldK Active %ldK)\n" "Free Memory:\t%ldK\n", vm->t_rq,vm->t_dw,vm->t_pw,vm->t_sl, vm->t_vm * kpp, vm->t_avm * kpp, vm->t_rm * kpp, vm->t_arm * kpp, vm->t_vmshr * kpp, vm->t_avmshr * kpp, vm->t_rmshr * kpp, vm->t_armshr * kpp, vm->t_free * kpp); } static SysctlVar *sysctl_var_new ( const gchar *name ) { int mib[CTL_MAXNAME]; int qoid[CTL_MAXNAME+2]; guchar buf[BUFSIZ]; size_t len = CTL_MAXNAME; size_t qlen = BUFSIZ; SysctlVar *var; var = g_malloc0(sizeof(SysctlVar)); if(sysctlnametomib(name,mib,&len)<0) return var; qoid[0] = 0; qoid[1] = 4; memcpy(qoid+2,mib,len*sizeof(int)); if(sysctl(qoid,len+2,buf,&qlen,0,0)<0) return var; var->len = len; var->oid = g_memdup2(mib,len*sizeof(int)); var->type = *(u_int *)buf & CTLTYPE; if(var->type == CTLTYPE_OPAQUE) { var->func = NULL; if(!g_strcmp0((char *)buf+sizeof(u_int),"S,clockinfo")) var->func = sysctl_clockinfo; else if(!g_strcmp0((char *)buf+sizeof(u_int),"S,timeval")) var->func = sysctl_timeval; else if(!g_strcmp0((char *)buf+sizeof(u_int),"S,loadavg")) var->func = sysctl_loadavg; else if(!g_strcmp0((char *)buf+sizeof(u_int),"S,vmtotal")) var->func = sysctl_vmtotal; } return var; } static gchar *sysctl_query ( SysctlVar *var ) { u_char buf[1024]; void *ptr,*vptr; size_t ilen, nlen = sizeof(buf); gchar *res, *tmp; if(!var || !var->oid) return g_strdup("Sysctl invalid variable"); if(sysctl(var->oid,var->len,buf,&nlen,0,0)<0) return g_strdup("Unsuccessful sysctl call"); ptr = buf; res = g_strdup(""); while((int)nlen>0) { vptr = NULL; switch(var->type) { #ifdef CTLTYPE_STRING case CTLTYPE_STRING: vptr = g_strndup(ptr,nlen); ilen = nlen; break; #endif #ifdef CTLTYPE_INT case CTLTYPE_INT: if(nlen>=sizeof(int)) vptr = g_strdup_printf("%d",*(int *)ptr); ilen=sizeof(int); break; #endif #ifdef CTLTYPE_UINT case CTLTYPE_UINT: if(nlen>=sizeof(u_int)) vptr = g_strdup_printf("%u",*(u_int *)ptr); ilen=sizeof(u_int); break; #endif #ifdef CTLTYPE_LONG case CTLTYPE_LONG: if(nlen>=sizeof(long)) vptr = g_strdup_printf("%ld",*(long *)ptr); ilen=sizeof(long); break; #endif #ifdef CTLTYPE_ULONG case CTLTYPE_ULONG: if(nlen>=sizeof(u_long)) vptr = g_strdup_printf("%lu",*(u_long *)ptr); ilen=sizeof(u_long); break; #endif #ifdef CTLTYPE_S8 case CTLTYPE_S8: if(nlen>=sizeof(int8_t)) vptr = g_strdup_printf("%d",*(int8_t *)ptr); ilen=sizeof(int8_t); break; #endif #ifdef CTLTYPE_U8 case CTLTYPE_U8: if(nlen>=sizeof(uint8_t)) vptr = g_strdup_printf("%u",*(uint8_t *)ptr); ilen=sizeof(uint8_t); break; #endif #ifdef CTLTYPE_S16 case CTLTYPE_S16: if(nlen>=sizeof(int16_t)) vptr = g_strdup_printf("%d",*(int16_t *)ptr); ilen=sizeof(int16_t); break; #endif #ifdef CTLTYPE_U16 case CTLTYPE_U16: if(nlen>=sizeof(uint16_t)) vptr = g_strdup_printf("%u",*(uint16_t *)ptr); ilen=sizeof(uint16_t); break; #endif #ifdef CTLTYPE_S32 case CTLTYPE_S32: if(nlen>=sizeof(int32_t)) vptr = g_strdup_printf("%d",*(int32_t *)ptr); ilen=sizeof(int32_t); break; #endif #ifdef CTLTYPE_U32 case CTLTYPE_U32: if(nlen>=sizeof(uint32_t)) vptr = g_strdup_printf("%u",*(uint32_t *)ptr); ilen=sizeof(uint32_t); break; #endif #ifdef CTLTYPE_S64 case CTLTYPE_S64: if(nlen>=sizeof(int64_t)) vptr = g_strdup_printf("%ld",*(int64_t *)ptr); ilen=sizeof(int64_t); break; #endif #ifdef CTLTYPE_U64 case CTLTYPE_U64: if(nlen>=sizeof(uint64_t)) vptr = g_strdup_printf("%lu",*(uint64_t *)ptr); ilen=sizeof(uint64_t); break; #endif #ifdef CTLTYPE_OPAQUE case CTLTYPE_OPAQUE: if(var->func) vptr = var->func(buf,nlen); ilen=nlen; break; #endif default: vptr = g_strdup("[unknown]"); ilen=nlen; break; } if(vptr) { tmp = g_strconcat(res,*res?" ":"",vptr,NULL); g_free(vptr); g_free(res); res = tmp; } nlen-=ilen; ptr+=ilen; } return res; } static SysctlVar *sysctl_var_get ( gchar *name ) { static GHashTable *index; SysctlVar *var; if(!index) index = g_hash_table_new(g_str_hash, g_str_equal); var = g_hash_table_lookup(index, name); if(!var) { var = sysctl_var_new(name); g_hash_table_insert(index,name,var); } return var; } void *bsdctl_func ( void **params, void *widget, void *event ) { SysctlVar *var; if(!params || !params[0]) return g_strdup(""); var = sysctl_var_get(params[0]); return sysctl_query(var); } ModuleExpressionHandlerV1 handler1 = { .flags = 0, .name = "BSDCtl", .parameters = "S", .function = bsdctl_func }; ModuleExpressionHandlerV1 *sfwbar_expression_handlers[] = { &handler1, NULL }; sfwbar-1.0~beta13/modules/idleinhibit.c000066400000000000000000000060461450657570000201530ustar00rootroot00000000000000/* This entire file is licensed under GNU General Public License v3.0 * * Copyright 2023- Sfwbar maintainers */ #include #include #include #include "../src/module.h" #include "idle-inhibit-unstable-v1.h" static ModuleApiV1 *sfwbar_module_api; gint64 sfwbar_module_signature = 0x73f4d956a1; guint16 sfwbar_module_version = 1; static struct zwp_idle_inhibit_manager_v1 *idle_inhibit_manager; static void handle_global(void *data, struct wl_registry *registry, uint32_t name, const gchar *interface, uint32_t version) { if (!g_strcmp0(interface,zwp_idle_inhibit_manager_v1_interface.name)) idle_inhibit_manager = wl_registry_bind(registry, name, &zwp_idle_inhibit_manager_v1_interface,1); } static void handle_global_remove(void *data, struct wl_registry *registry, uint32_t name) { } static const struct wl_registry_listener registry_listener = { .global = handle_global, .global_remove = handle_global_remove }; void sfwbar_module_init ( ModuleApiV1 *api ) { struct wl_display *wdisp; struct wl_registry *registry; sfwbar_module_api = api; wdisp = gdk_wayland_display_get_wl_display(gdk_display_get_default()); if(!wdisp) g_message("Idle inhibit module: can't get wayland display\n"); registry = wl_display_get_registry(wdisp); wl_registry_add_listener(registry, ®istry_listener, NULL); wl_display_roundtrip(wdisp); } void *idle_inhibit_expr_func ( void **params, void *widget, void *event ) { if(widget && g_object_get_data(G_OBJECT(widget),"inhibitor")) return g_strdup("on"); else return g_strdup("off"); } static void idle_inhibitor_action ( gchar *act, gchar *dummy, void *widget, void *d2, void *d3, void *d4 ) { struct wl_surface *surface; struct zwp_idle_inhibitor_v1 *inhibitor; gboolean inhibit; inhibitor = g_object_get_data(G_OBJECT(widget),"inhibitor"); if(!g_ascii_strcasecmp(act,"on")) inhibit = TRUE; else if(!g_ascii_strcasecmp(act,"on")) inhibit = FALSE; else if(!g_ascii_strcasecmp(act,"toggle")) inhibit = (inhibitor == NULL); else return; if(inhibit && !inhibitor) { surface = gdk_wayland_window_get_wl_surface( gtk_widget_get_window(widget)); if(!surface) return; inhibitor = zwp_idle_inhibit_manager_v1_create_inhibitor( idle_inhibit_manager, surface ); g_object_set_data(G_OBJECT(widget),"inhibitor",inhibitor); MODULE_TRIGGER_EMIT("idleinhibitor"); } else if( !inhibit && inhibitor ) { g_object_set_data(G_OBJECT(widget),"inhibitor",NULL); zwp_idle_inhibitor_v1_destroy(inhibitor); MODULE_TRIGGER_EMIT("idleinhibitor"); } } ModuleExpressionHandlerV1 handler1 = { .flags = 0, .name = "IdleInhibitState", .parameters = "", .function = idle_inhibit_expr_func }; ModuleExpressionHandlerV1 *sfwbar_expression_handlers[] = { &handler1, NULL }; ModuleActionHandlerV1 act_handler1 = { .name = "SetIdleInhibitor", .function = idle_inhibitor_action }; ModuleActionHandlerV1 *sfwbar_action_handlers[] = { &act_handler1, NULL }; sfwbar-1.0~beta13/modules/mpd.c000066400000000000000000000154611450657570000164500ustar00rootroot00000000000000/* This entire file is licensed under GNU General Public License v3.0 * * Copyright 2022 Sfwbar maintainers */ #include #include #include "../src/module.h" #include "stdio.h" static ModuleApiV1 *sfwbar_module_api; gint64 sfwbar_module_signature = 0x73f4d956a1; guint16 sfwbar_module_version = 1; static struct mpd_status *status; static struct mpd_song *song; static struct mpd_connection *conn; static gchar *password; static guint64 last_update; static gboolean timer; static gboolean mpd_connect ( gpointer data ); static gboolean mpd_timer ( gpointer data ) { static guint64 last, current; if( !status || mpd_status_get_state(status)!=MPD_STATE_PLAY ) { timer = FALSE; return FALSE; } current = g_get_monotonic_time(); if((current-last)/mpd_status_get_total_time(status)/10 > 1) MODULE_TRIGGER_EMIT("mpd-progress"); return TRUE; } static gboolean mpd_update ( void ) { if(status) mpd_status_free(status); if(song) mpd_song_free(song); song = NULL; status = mpd_run_status (conn); if(!mpd_response_finish(conn)) return FALSE; song = mpd_run_current_song(conn); if(!mpd_response_finish(conn)) return FALSE; last_update = g_get_monotonic_time(); if(!timer && (!status || mpd_status_get_state(status) == MPD_STATE_PLAY)) { g_timeout_add(1000,mpd_timer,NULL); timer = 1; } MODULE_TRIGGER_EMIT("mpd"); return TRUE; } static gboolean mpd_event ( GIOChannel *chan, GIOCondition cond, void *d) { g_debug("MPD client: processing an event"); mpd_recv_idle(conn,FALSE); mpd_response_finish(conn); if( !mpd_update() ) { mpd_connection_free(conn); conn = NULL; g_timeout_add (1000,(GSourceFunc )mpd_connect,NULL); MODULE_TRIGGER_EMIT("mpd"); return FALSE; } mpd_send_idle_mask(conn,MPD_IDLE_PLAYER | MPD_IDLE_OPTIONS); return TRUE; } static gboolean mpd_connect ( gpointer data ) { GIOChannel *chan; conn = mpd_connection_new(NULL,0,0); if(conn && mpd_connection_get_error(conn) != MPD_ERROR_SUCCESS) { mpd_connection_free(conn); conn = NULL; } if(!conn) return TRUE; if(password) mpd_send_password(conn, password); g_debug("MPD client: connected to server (fd = %d)", mpd_connection_get_fd(conn)); mpd_update(); mpd_send_idle_mask(conn,MPD_IDLE_PLAYER | MPD_IDLE_OPTIONS); chan = g_io_channel_unix_new(mpd_connection_get_fd(conn)); g_io_add_watch(chan,G_IO_IN,(GIOFunc)mpd_event,conn); g_io_channel_unref(chan); return FALSE; } static void mpd_bool_set( bool (*get)(const struct mpd_status *), bool (*set)(struct mpd_connection *, bool), gchar *val ) { gboolean new; if(!conn || !status || !get || !set || !val) return; while(*val && g_ascii_isspace(*val)) val++; if(!g_ascii_strcasecmp(val,"on")) new = TRUE; else if(!g_ascii_strcasecmp(val,"off")) new = FALSE; else if(!g_ascii_strcasecmp(val,"toggle")) new = !get(status); else return; set(conn,new); } void sfwbar_module_init ( ModuleApiV1 *api ) { sfwbar_module_api = api; if(mpd_connect(NULL)) g_timeout_add (1000,(GSourceFunc )mpd_connect,NULL); } void *mpd_expr_func ( void **params, void *widget, void *event ) { if(!conn | !status | !song) return g_strdup("disconnected"); if(!params || !params[0]) return g_strdup(""); if(!g_ascii_strcasecmp(params[0],"title")) return g_strdup(song?mpd_song_get_tag(song,MPD_TAG_TITLE,0):""); else if(!g_ascii_strcasecmp(params[0],"track")) return g_strdup(song?mpd_song_get_tag(song,MPD_TAG_TRACK,0):""); else if(!g_ascii_strcasecmp(params[0],"artist")) return g_strdup(song?mpd_song_get_tag(song,MPD_TAG_ARTIST,0):""); else if(!g_ascii_strcasecmp(params[0],"album")) return g_strdup(song?mpd_song_get_tag(song,MPD_TAG_ALBUM,0):""); else if(!g_ascii_strcasecmp(params[0],"genre")) return g_strdup(song?mpd_song_get_tag(song,MPD_TAG_GENRE,0):""); else if(!g_ascii_strcasecmp(params[0],"volume")) return g_strdup_printf("%d",status?mpd_status_get_volume(status):0); else if(!g_ascii_strcasecmp(params[0],"repeat")) return g_strdup_printf("%d",status?mpd_status_get_repeat(status):0); else if(!g_ascii_strcasecmp(params[0],"random")) return g_strdup_printf("%d",status?mpd_status_get_random(status):0); else if(!g_ascii_strcasecmp(params[0],"queue_len")) return g_strdup_printf("%d",status?mpd_status_get_queue_length(status):0); else if(!g_ascii_strcasecmp(params[0],"queue_pos")) return g_strdup_printf("%d",status?mpd_status_get_song_pos(status):0); else if(!g_ascii_strcasecmp(params[0],"elapsed")) return g_strdup_printf("%lu",status?mpd_status_get_elapsed_ms(status) + (mpd_status_get_state(status)==MPD_STATE_PLAY? (g_get_monotonic_time()-last_update)/1000:0):0); else if(!g_ascii_strcasecmp(params[0],"length")) return g_strdup_printf("%u",status?mpd_status_get_total_time(status):0); else if(!g_ascii_strcasecmp(params[0],"rate")) return g_strdup_printf("%u",status?mpd_status_get_kbit_rate(status):0); else if(!g_ascii_strcasecmp(params[0],"state")) switch(mpd_status_get_state(status)) { case MPD_STATE_PLAY: return g_strdup("play"); case MPD_STATE_PAUSE: return g_strdup("pause"); case MPD_STATE_STOP: return g_strdup("stop"); default: return g_strdup("unknown"); } return g_strdup("Invalid request"); } static void mpd_command ( gchar *cmd, gchar *dummy, void *d1, void *d2, void *d3, void *d4 ) { if(!conn) return; mpd_run_noidle(conn); if(!g_ascii_strcasecmp(cmd,"play")) mpd_run_play(conn); else if(!g_ascii_strcasecmp(cmd,"prev")) mpd_run_previous(conn); else if(!g_ascii_strcasecmp(cmd,"next")) mpd_run_next(conn); else if(!g_ascii_strcasecmp(cmd,"pause")) mpd_run_toggle_pause(conn); else if(!g_ascii_strcasecmp(cmd,"stop")) mpd_run_stop(conn); else if(!g_ascii_strncasecmp(cmd,"random",6)) mpd_bool_set(mpd_status_get_random, mpd_run_random, cmd+6); else if(!g_ascii_strncasecmp(cmd,"repeat",6)) mpd_bool_set(mpd_status_get_repeat, mpd_run_repeat, cmd+6); mpd_response_finish(conn); mpd_send_idle_mask(conn,MPD_IDLE_PLAYER | MPD_IDLE_OPTIONS); } static void mpd_set_passwd ( gchar *pwd, gchar *dummy, void *d1, void *d2, void *d3, void *d4 ) { g_free(password); if(pwd && *pwd) password = g_strdup(pwd); else password = NULL; } ModuleExpressionHandlerV1 handler1 = { .flags = 0, .name = "Mpd", .parameters = "S", .function = mpd_expr_func }; ModuleExpressionHandlerV1 *sfwbar_expression_handlers[] = { &handler1, NULL }; ModuleActionHandlerV1 act_handler1 = { .name = "MpdSetPassword", .function = mpd_set_passwd }; ModuleActionHandlerV1 act_handler2 = { .name = "MpdCommand", .function = mpd_command }; ModuleActionHandlerV1 *sfwbar_action_handlers[] = { &act_handler1, &act_handler2, NULL }; sfwbar-1.0~beta13/modules/network.c000066400000000000000000000457431450657570000173670ustar00rootroot00000000000000/* This entire file is licensed under GNU General Public License v3.0 * * Copyright 2022 Sfwbar maintainers */ #include "../src/module.h" #include #include #include #include #include #include typedef struct _iface_info { gchar *name; GMutex mutex; gboolean invalid; struct in_addr ip, mask, bcast, gateway; struct in6_addr ip6, mask6, bcast6, gateway6; guint32 rx_packets, tx_packets, rx_bytes, tx_bytes; guint32 prx_packets, ptx_packets, prx_bytes, ptx_bytes; gint64 last_time, time_diff; gchar *essid; } iface_info; iface_info *route; ModuleApiV1 *sfwbar_module_api; gint64 sfwbar_module_signature = 0x73f4d956a1; guint16 sfwbar_module_version = 1; guint32 seq; GList *iface_list; gint qual, level, noise; static void net_update_essid ( gchar * ); static iface_info *net_iface_from_name ( gchar *name, gboolean create ) { GList *iter; iface_info *iface; for(iter = iface_list;iter;iter=g_list_next(iter)) if(!g_strcmp0(((iface_info *)iter->data)->name,name)) return iter->data; if(!create) return NULL; iface = g_malloc0(sizeof(iface_info)); g_mutex_init(&iface->mutex); iface->name = g_strdup(name); iface_list = g_list_prepend(iface_list,iface); return iface; } static gchar *net_get_cidr ( guint32 addr ) { gint i; guint32 m; m = g_ntohl(addr); for(i=31;(i>=0)&&(m&0x1<ifa_next) { iface = net_iface_from_name(iter->ifa_name,TRUE); if(iter->ifa_addr) switch(iter->ifa_addr->sa_family) { case AF_INET: iface->ip = ((struct sockaddr_in *)(iter->ifa_addr))->sin_addr; if(iter->ifa_netmask) iface->mask = ((struct sockaddr_in *)(iter->ifa_netmask))->sin_addr; if(iter->ifa_broadaddr) iface->bcast = ((struct sockaddr_in *)(iter->ifa_broadaddr))->sin_addr; break; case AF_INET6: iface->ip6 = ((struct sockaddr_in6 *)(iter->ifa_addr))->sin6_addr; if(iter->ifa_netmask) iface->mask6 = ((struct sockaddr_in6 *)(iter->ifa_netmask))->sin6_addr; if(iter->ifa_broadaddr) iface->bcast6 = ((struct sockaddr_in6 *)(iter->ifa_broadaddr))->sin6_addr; break; } } freeifaddrs(addrs); } static void net_set_interface ( gint32 iidx, struct in_addr gate, struct in6_addr gate6 ) { gchar ifname[IF_NAMESIZE]; iface_info *iface; if(!iidx) { if(route) { route->gateway.s_addr = 0; memset(&route->gateway6,0,sizeof(route->gateway6)); route = NULL; MODULE_TRIGGER_EMIT("network"); } return; } iface = net_iface_from_name(if_indextoname(iidx,ifname),TRUE); g_mutex_lock(&iface->mutex); g_free(iface->name); iface->name = g_strdup(ifname); iface->gateway = gate; iface->gateway6 = gate6; g_mutex_unlock(&iface->mutex); net_update_essid(ifname); net_update_ifaddrs(); route = iface; MODULE_TRIGGER_EMIT("network"); } static gchar *net_getaddr ( void *ina, gint type ) { gchar addr[INET6_ADDRSTRLEN]; return g_strdup(inet_ntop(type,ina,addr,INET_ADDRSTRLEN)); } #if defined(__linux__) #include #include static void net_update_traffic ( gchar *interface ) { gint64 ctime; struct ifaddrs *addrs, *iter; struct rtnl_link_stats *stats; iface_info *iface; iface = net_iface_from_name(interface,FALSE); if(!iface || !iface->invalid) return; getifaddrs(&addrs); for(iter=addrs;iter;iter=iter->ifa_next) { if(!g_strcmp0(interface,iter->ifa_name) && iter->ifa_addr->sa_family == AF_PACKET) { iface->prx_packets = iface->rx_packets; iface->ptx_packets = iface->tx_packets; iface->prx_bytes = iface->rx_bytes; iface->ptx_bytes = iface->tx_bytes; stats = ((struct rtnl_link_stats *)(iter->ifa_data)); iface->rx_packets = stats->rx_packets; iface->tx_packets = stats->tx_packets; iface->rx_bytes = stats->rx_bytes; iface->tx_bytes = stats->tx_bytes; ctime = g_get_monotonic_time(); iface->time_diff = ctime - iface->last_time; iface->last_time = ctime; iface->invalid = FALSE; } } freeifaddrs(addrs); } static void net_update_essid ( gchar *interface ) { struct iwreq wreq; gint sock; gchar lessid[IW_ESSID_MAX_SIZE+1]; iface_info *iface; if(!interface) return; iface = net_iface_from_name(interface,FALSE); if(!iface) return; *lessid=0; memset(&wreq,0,sizeof(wreq)); wreq.u.essid.length = IW_ESSID_MAX_SIZE+1; wreq.u.essid.pointer = lessid; (void)g_strlcpy(wreq.ifr_name,interface,sizeof(wreq.ifr_name)); sock = socket(AF_INET, SOCK_DGRAM, 0); if(sock >= 0 && ioctl(sock, SIOCGIWESSID, &wreq) >= 0) { g_mutex_lock(&iface->mutex); g_free(iface->essid); iface->essid = g_strdup(lessid); g_mutex_unlock(&iface->mutex); } if(sock >= 0) close(sock); } static gdouble net_get_signal ( gchar *interface ) { struct iw_statistics wstats; struct iwreq wreq; gint sock; if(!interface) return 0.0; memset(&wreq,0,sizeof(wreq)); wreq.u.data.pointer = &wstats; wreq.u.data.length = sizeof(wstats); wreq.u.data.flags = 1; (void)g_strlcpy(wreq.ifr_name,interface,sizeof(wreq.ifr_name)); sock = socket(AF_INET, SOCK_DGRAM, 0); if(sock >= 0 && ioctl(sock, SIOCGIWSTATS, &wreq) >= 0) { qual = wstats.qual.qual; level = wstats.qual.level-(wstats.qual.updated&IW_QUAL_DBM?0x100:0); noise = wstats.qual.noise-(wstats.qual.updated&IW_QUAL_DBM?0x100:0); } if(sock >= 0) close(sock); return CLAMP(2*(level+100),0,100); } static gint net_rt_connect ( void ) { gint sock; struct sockaddr_nl saddr; sock = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE); if(sock < 0) return sock; saddr.nl_family = AF_NETLINK; saddr.nl_pid = getpid(); saddr.nl_groups = RTMGRP_LINK | RTMGRP_IPV4_IFADDR | RTMGRP_IPV4_ROUTE; if(bind(sock,(struct sockaddr *)&saddr,sizeof(saddr)) >= 0) return sock; close(sock); return -1; } static gint net_rt_request ( gint sock ) { struct { struct nlmsghdr hdr; struct rtmsg rtm; } nlreq; memset(&nlreq,0,sizeof(nlreq)); nlreq.hdr.nlmsg_type = RTM_GETROUTE; nlreq.hdr.nlmsg_pid = getpid(); nlreq.hdr.nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP; nlreq.hdr.nlmsg_len = sizeof(nlreq); nlreq.hdr.nlmsg_seq = seq++; nlreq.rtm.rtm_family = AF_INET; return send(sock,&nlreq,sizeof(nlreq),0); } static gboolean net_rt_parse (GIOChannel *chan, GIOCondition cond, gpointer d) { struct nlmsghdr *hdr; struct rtattr *rta; gint sock; gint rtl; gchar buf[4096]; gchar ifname[IF_NAMESIZE]; gint len; gint32 iidx; struct in_addr dest, gate; struct in6_addr gate6; struct ifinfomsg *ifmsg; sock = g_io_channel_unix_get_fd(chan); len = recv(sock,buf,sizeof(buf),0); hdr = (struct nlmsghdr *)buf; if(len<=0) return TRUE; for(;NLMSG_OK(hdr,len)&&hdr->nlmsg_type!=NLMSG_DONE;hdr=NLMSG_NEXT(hdr,len)) { dest.s_addr = 0; gate.s_addr = 0; memset(&gate6,0,sizeof(gate6)); iidx = 0; if(hdr->nlmsg_type == RTM_DELADDR && route && !g_strcmp0(route->name,if_indextoname( ((struct ifinfomsg *)NLMSG_DATA(hdr))->ifi_index,ifname))) { net_set_interface(0,gate,gate6); break; } else if(hdr->nlmsg_type == RTM_NEWLINK) { rta = IFLA_RTA(NLMSG_DATA(hdr)); rtl = IFLA_PAYLOAD(hdr); ifmsg = (struct ifinfomsg *)NLMSG_DATA(hdr); if(ifmsg->ifi_change!=0) for(;rtl && RTA_OK(rta,rtl);rta=RTA_NEXT(rta,rtl)) if(rta->rta_type==IFLA_WIRELESS) net_update_essid(if_indextoname(ifmsg->ifi_index,ifname)); break; } else if(hdr->nlmsg_type == RTM_NEWROUTE || hdr->nlmsg_type == RTM_DELROUTE) { rta = RTM_RTA(NLMSG_DATA(hdr)); rtl = RTM_PAYLOAD(hdr); for(;rtl && RTA_OK(rta,rtl);rta=RTA_NEXT(rta,rtl)) switch(rta->rta_type) { case RTA_DST: if(((struct rtmsg *)NLMSG_DATA(hdr))->rtm_family==AF_INET) dest = *(struct in_addr *)RTA_DATA(rta); break; case RTA_GATEWAY: if(((struct rtmsg *)NLMSG_DATA(hdr))->rtm_family==AF_INET) gate = *(struct in_addr *)RTA_DATA(rta); else if(((struct rtmsg *)NLMSG_DATA(hdr))->rtm_family==AF_INET6) gate6 = *(struct in6_addr *)RTA_DATA(rta); break; case RTA_OIF: iidx = *(guint32 *)RTA_DATA(rta); break; } if(hdr->nlmsg_type==RTM_NEWROUTE && !dest.s_addr && gate.s_addr && iidx && !(((struct rtmsg *)NLMSG_DATA(hdr))->rtm_dst_len)) net_set_interface(iidx, gate,gate6); else if(hdr->nlmsg_type==RTM_DELROUTE && !dest.s_addr && iidx && !(((struct rtmsg *)NLMSG_DATA(hdr))->rtm_dst_len)) net_set_interface(0, gate,gate6); } } return TRUE; } #elif defined(__FreeBSD__) || defined(__OpenBSD__) #include #include static void net_update_traffic ( gchar *interface ) { gint64 ctime; struct ifaddrs *addrs, *iter; iface_info *iface; iface = net_iface_from_name(interface,FALSE); if(!iface || !iface->invalid) return; getifaddrs(&addrs); for(iter=addrs;iter;iter=iter->ifa_next) { if(!g_strcmp0(interface,iter->ifa_name) && iter->ifa_addr && iter->ifa_addr->sa_family==AF_LINK) { iface->prx_packets = iface->rx_packets; iface->ptx_packets = iface->tx_packets; iface->prx_bytes = iface->rx_bytes; iface->ptx_bytes = iface->tx_bytes; iface->rx_packets = ((struct if_data *)(iter->ifa_data))->ifi_ipackets; iface->tx_packets = ((struct if_data *)(iter->ifa_data))->ifi_opackets; iface->rx_bytes = ((struct if_data *)(iter->ifa_data))->ifi_ibytes; iface->tx_bytes = ((struct if_data *)(iter->ifa_data))->ifi_obytes; ctime = g_get_monotonic_time(); iface->time_diff = ctime - iface->last_time; iface->last_time = ctime; iface->invalid = FALSE; } } freeifaddrs(addrs); } #if defined(__FreeBSD__) #include static void net_update_essid ( char *interface ) { struct ieee80211req req; iface_info *iface; gchar lessid[IEEE80211_NWID_LEN+1]; gint sock; iface = net_iface_from_name(interface,FALSE); if(!iface) return; sock = socket(AF_INET, SOCK_DGRAM, 0); if(sock<0) return; memset(&req,0,sizeof(req)); req.i_type = IEEE80211_IOC_SSID; req.i_data = lessid; req.i_len = sizeof(lessid); (void)g_strlcpy(req.i_name,interface,sizeof(req.i_name)); if(ioctl(sock,SIOCG80211,&req) >=0) { lessid[MIN(req.i_len,IEEE80211_NWID_LEN)]='\0'; g_mutex_lock(&iface->mutex); g_free(iface->essid); iface->essid = g_strdup(lessid); g_mutex_unlock(&iface->mutex); } else { g_mutex_lock(&iface->mutex); g_free(iface->essid); iface->essid = NULL; g_mutex_unlock(&iface->mutex); } close(sock); } static gdouble net_get_signal ( gchar *interface ) { struct ieee80211req req; union { struct ieee80211req_sta_req sreq; gchar buf[24*1024]; } nfo; gchar bssid[IEEE80211_ADDR_LEN]; gint sock; gint rssi; if(!interface) return 0.0; memset(&req,0,sizeof(req)); req.i_type = IEEE80211_IOC_BSSID; req.i_data = bssid; req.i_len = IEEE80211_ADDR_LEN; (void)g_strlcpy(req.i_name,interface,sizeof(req.i_name)); sock = socket(AF_INET, SOCK_DGRAM, 0); if(sock<0) return 0; if(ioctl(sock,SIOCG80211,&req) < 0) { close(sock); return 0; } memset(&nfo,0,sizeof(nfo)); memcpy(nfo.sreq.is_u.macaddr,bssid,sizeof(bssid)); memset(&req,0,sizeof(req)); req.i_type = IEEE80211_IOC_STA_INFO; req.i_data = &nfo; req.i_len = sizeof(nfo); (void)g_strlcpy(req.i_name,interface,sizeof(req.i_name)); if(ioctl(sock,SIOCG80211,&req) >=0) rssi = nfo.sreq.info[0].isi_noise+nfo.sreq.info[0].isi_rssi/2; else rssi = -100; close(sock); return CLAMP(2*(rssi+100),0,100); } #else /* OpenBSD */ #include #include #include #include #include #include static gdouble net_get_signal ( gchar *interface ) { gint sock; struct ieee80211_bssid bssid; struct ieee80211_nodereq req; sock = socket(AF_INET,SOCK_DGRAM,0); if(sock <0) return 0; memset(&bssid,0,sizeof(bssid)); (void)g_strlcpy(bssid.i_name,interface,sizeof(bssid.i_name)); if(ioctl(sock,SIOCG80211BSSID,&bssd) < 0 || !*bssid.i_bssid || !memcmp(bssid.i_bssid,bssid.i_bssid+1,IEEE80211_ADDR_LEN-1)) { close(sock); return 0; } memset(&req,0,sizeof(req)); (void)g_strlcpy(req.nr_ifname,interface,sizeof(req.nr_ifname)); memcpy(&(req.nr_macaddr),bssid.i_bssid,sizeof(req.nr_macaddr)); if(ioctl(sock,SIOCG80211NODE,&req) >= 0) { if(g_mutex_trylock(&mutex)) { g_free(essid); essid = g_strdup(req.nr_nwid); g_mutex_unlock(&mutex); } if(req.nr_max_nssi) level = IEEE80211_NODEREQ_RSSI(&req); else level = CLAMP(2*(req.nr_rssi+100),0,100); } close(sock); return level; } static void net_update_essid ( gchar *interface ) { (void)net_get_signal(interface); } #endif static gint net_rt_connect ( void ) { return socket(AF_ROUTE, SOCK_RAW,0); } static gboolean net_rt_request ( gint sock ) { struct { struct rt_msghdr hdr; struct sockaddr_in sin; } rtmsg; memset(&rtmsg,0,sizeof(rtmsg)); rtmsg.hdr.rtm_msglen = sizeof(rtmsg); rtmsg.hdr.rtm_type = RTM_GET; rtmsg.hdr.rtm_version = RTM_VERSION; rtmsg.hdr.rtm_addrs = RTA_DST; rtmsg.hdr.rtm_seq = seq++; rtmsg.hdr.rtm_pid = getpid(); rtmsg.sin.sin_len = sizeof(struct sockaddr_in); rtmsg.sin.sin_family = AF_INET; rtmsg.sin.sin_addr.s_addr = 0; if(send(sock,&rtmsg,rtmsg.hdr.rtm_msglen,0) >= 0) return sock; close(sock); return -1; } static gboolean net_rt_parse (GIOChannel *chan, GIOCondition cond, gpointer d) { gint len ,i; gint sock; gchar buff[4096]; struct rt_msghdr *hdr; struct sockaddr *sa; struct in_addr gate, dest, mask; struct in6_addr gate6; gchar ifname[IF_NAMESIZE]; sock = g_io_channel_unix_get_fd(chan); len = recv(sock,&buff,sizeof(buff),0); if(len<=0) return TRUE; hdr = (struct rt_msghdr *)buff; gate.s_addr = 0; mask.s_addr = 1; dest.s_addr = 1; memset(&gate6,0,sizeof(gate6)); if(hdr->rtm_type == RTM_DELADDR && route && !g_strcmp0(route->name, if_indextoname(((struct ifa_msghdr *)hdr)->ifam_index,ifname))) { net_set_interface(0,gate,gate6); return TRUE; } else if(hdr->rtm_type==RTM_ADD || hdr->rtm_type==RTM_DELETE || (hdr->rtm_type == RTM_GET && hdr->rtm_pid == getpid())) { sa = (struct sockaddr *)(hdr +1); for(i=0;irtm_addrs & 1<sin_addr); else if(i == RTAX_NETMASK) mask = (((struct sockaddr_in *)sa)->sin_addr); else if(i == RTAX_GATEWAY) gate = (((struct sockaddr_in *)sa)->sin_addr); sa = (void *)sa+((sa->sa_len+sizeof(u_long)-1)&~((sizeof(u_long)-1))); } if(hdr->rtm_type==RTM_GET) net_set_interface(hdr->rtm_index,gate,gate6); else if(hdr->rtm_type==RTM_ADD && !dest.s_addr && gate.s_addr && !mask.s_addr && hdr->rtm_index) net_set_interface(hdr->rtm_index,gate,gate6); else if(hdr->rtm_type==RTM_DELETE && !dest.s_addr && !mask.s_addr && hdr->rtm_index) net_set_interface(0,gate,gate6); } return TRUE; } #else /* unknown platform, provide shims */ static gchar *net_getaddr ( void *ina, int type ) { return g_strdup(""); } static void net_update_traffic ( void ) { } static gdouble net_get_signal ( gchar *interface ) { return 0.0; } static void network_init ( void * ) { } #endif void sfwbar_module_init ( ModuleApiV1 *api ) { int sock; GIOChannel *chan; sfwbar_module_api = api; sock = net_rt_connect(); g_debug("network socket: %d",sock); if(sock >= 0 && net_rt_request(sock) >= 0) { chan = g_io_channel_unix_new(sock); g_io_add_watch(chan,G_IO_IN | G_IO_PRI |G_IO_ERR | G_IO_HUP, net_rt_parse,NULL); } else if(sock >= 0) close(sock); } void sfwbar_module_invalidate ( void ) { GList *iter; for(iter=iface_list;iter;iter=g_list_next(iter)) ((iface_info *)iter->data)->invalid = TRUE; } void *network_func_netstat ( void **params, void *widget, void *event ) { iface_info *iface; gdouble *result; result = g_malloc0(sizeof(gdouble)); if(!params || !params[0]) return result; if(params[1] && *((gchar *)params[1])) iface = net_iface_from_name(params[1],FALSE); else iface = route; if(!iface) return result; g_mutex_lock(&iface->mutex); if(!g_ascii_strcasecmp(params[0],"signal")) *result = net_get_signal(route?route->name:NULL); else if(!g_ascii_strcasecmp(params[0],"rxrate")) { net_update_traffic(iface->name); *result = (gdouble)(iface->rx_bytes-iface->prx_bytes)* 1000000/iface->time_diff; } else if(!g_ascii_strcasecmp(params[0],"txrate")) { net_update_traffic(iface->name); *result = (gdouble)(iface->tx_bytes-iface->ptx_bytes)* 1000000/iface->time_diff; } g_mutex_unlock(&iface->mutex); return result; } void *network_func_netinfo ( void **params, void *widget, void *event ) { gchar *result; iface_info *iface; if(!params || !params[0]) return g_strdup(""); if(params[1] && *((gchar *)params[1])) iface = net_iface_from_name(params[1],FALSE); else iface = route; if(!iface) return g_strdup(""); g_mutex_lock(&iface->mutex); if(!g_ascii_strcasecmp(params[0],"ip")) result = net_getaddr(&iface->ip,AF_INET); else if(!g_ascii_strcasecmp(params[0],"mask")) result = net_getaddr(&iface->mask,AF_INET); else if(!g_ascii_strcasecmp(params[0],"cidr")) result = net_get_cidr(iface->mask.s_addr); else if(!g_ascii_strcasecmp(params[0],"ip6")) result = net_getaddr(&iface->ip6,AF_INET6); else if(!g_ascii_strcasecmp(params[0],"mask6")) result = net_getaddr(&iface->mask6,AF_INET6); else if(!g_ascii_strcasecmp(params[0],"gateway")) result = net_getaddr(&iface->gateway,AF_INET); else if(!g_ascii_strcasecmp(params[0],"gateway6")) result = net_getaddr(&iface->gateway6,AF_INET6); else if(!g_ascii_strcasecmp(params[0],"essid")) result = g_strdup(iface->essid?iface->essid:""); else if(!g_ascii_strcasecmp(params[0],"interface")) result = g_strdup(iface->name); else result = g_strdup("invalid query"); g_mutex_unlock(&iface->mutex); return result; } ModuleExpressionHandlerV1 handler1 = { .flags = 0, .name = "NetInfo", .parameters = "Ss", .function = network_func_netinfo }; ModuleExpressionHandlerV1 handler2 = { .flags = MODULE_EXPR_NUMERIC, .name = "NetStat", .parameters = "Ss", .function = network_func_netstat }; ModuleExpressionHandlerV1 *sfwbar_expression_handlers[] = { &handler1, &handler2, NULL }; sfwbar-1.0~beta13/modules/pulsectl.c000066400000000000000000000224031450657570000175150ustar00rootroot00000000000000/* This entire file is licensed under GNU General Public License v3.0 * * Copyright 2022 Sfwbar maintainers */ #include #include "../src/module.h" #include #include ModuleApiV1 *sfwbar_module_api; gboolean invalid; typedef struct _pulse_info { guint32 idx; gchar *name; gboolean mute; pa_cvolume cvol; gdouble vol; gchar *icon, *form, *port, *monitor; } pulse_info; GList *sink_list, *source_list; pa_mainloop_api *papi; gint64 sfwbar_module_signature = 0x73f4d956a1; guint16 sfwbar_module_version = 1; pa_context *pctx; gchar *sink_name, *source_name; gboolean fixed_sink, fixed_source; static pulse_info *pulse_info_from_name ( GList **l, const gchar *name, gboolean new ) { GList *iter; pulse_info *info; if(name) for(iter=*l;iter;iter=g_list_next(iter)) if(!g_strcmp0(((pulse_info *)iter->data)->name,name)) return iter->data; if(new) { info = g_malloc0(sizeof(pulse_info)); *l = g_list_prepend(*l,info); return info; } else return NULL; } static void pulse_set_sink ( const gchar *sink, gboolean fixed ) { if(!fixed && fixed_sink) return; fixed_sink = fixed; g_free(sink_name); sink_name = g_strdup(sink); } static void pulse_set_source ( const gchar *source, gboolean fixed ) { if(!fixed && fixed_source) return; fixed_source = fixed; g_free(source_name); source_name = g_strdup(source); } static void pulse_sink_cb ( pa_context *ctx, const pa_sink_info *pinfo, gint eol, void *d ) { pulse_info *info; if(!pinfo) return; info = pulse_info_from_name(&sink_list,pinfo->name,TRUE); g_free(info->name); info->name = g_strdup(pinfo->name); g_free(info->icon); info->icon = g_strdup(pa_proplist_gets(pinfo->proplist, PA_PROP_DEVICE_ICON_NAME)); g_free(info->form); info->form = g_strdup(pa_proplist_gets(pinfo->proplist, PA_PROP_DEVICE_FORM_FACTOR)); g_free(info->port); info->port = g_strdup(pinfo->active_port?pinfo->active_port->name:NULL); g_free(info->monitor); info->monitor = g_strdup(pinfo->monitor_source_name); info->idx = pinfo->index; info->cvol = pinfo->volume; info->vol = 100.0 * pa_cvolume_avg(&info->cvol)/PA_VOLUME_NORM; info->mute = pinfo->mute; MODULE_TRIGGER_EMIT("pulse"); } static void pulse_source_cb ( pa_context *ctx, const pa_source_info *pinfo, gint eol, void *d ) { pulse_info *info; if(!pinfo) return; info = pulse_info_from_name(&source_list,pinfo->name,TRUE); g_free(info->name); info->name = g_strdup(pinfo->name); g_free(info->icon); info->icon = g_strdup(pa_proplist_gets(pinfo->proplist, PA_PROP_DEVICE_ICON_NAME)); g_free(info->form); info->form = g_strdup(pa_proplist_gets(pinfo->proplist, PA_PROP_DEVICE_FORM_FACTOR)); g_free(info->port); info->port = g_strdup(pinfo->active_port?pinfo->active_port->name:"Unknown"); g_free(info->monitor); info->monitor = g_strdup(pinfo->monitor_of_sink_name); info->idx = pinfo->index; info->cvol = pinfo->volume; info->vol = 100.0 * pa_cvolume_avg(&info->cvol)/PA_VOLUME_NORM; info->mute = pinfo->mute; MODULE_TRIGGER_EMIT("pulse"); } static void pulse_server_cb ( pa_context *ctx, const pa_server_info *info, void *d ) { pulse_set_sink(info->default_sink_name,FALSE); pulse_set_source(info->default_source_name,FALSE); pa_operation_unref( pa_context_get_sink_info_list(ctx,pulse_sink_cb,NULL)); pa_operation_unref( pa_context_get_source_info_list(ctx,pulse_source_cb,NULL)); } static void pulse_subscribe_cb ( pa_context *ctx, pa_subscription_event_type_t type, guint idx, void *d ) { if(!(type & PA_SUBSCRIPTION_EVENT_CHANGE)) return; switch(type & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) { case PA_SUBSCRIPTION_EVENT_SERVER: pa_operation_unref( pa_context_get_server_info(ctx,pulse_server_cb,NULL)); break; case PA_SUBSCRIPTION_EVENT_SINK: pa_operation_unref( pa_context_get_sink_info_by_index(ctx,idx,pulse_sink_cb,NULL)); break; case PA_SUBSCRIPTION_EVENT_SINK_INPUT: break; case PA_SUBSCRIPTION_EVENT_SOURCE: pa_operation_unref( pa_context_get_source_info_by_index(ctx,idx,pulse_source_cb,NULL)); break; case PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT: break; } } static void pulse_state_cb ( pa_context *ctx, gpointer data ) { pa_context_state_t state; state = pa_context_get_state(ctx); if(state == PA_CONTEXT_FAILED || state == PA_CONTEXT_TERMINATED) papi->quit(papi,0); else if(state == PA_CONTEXT_READY) { pa_operation_unref( pa_context_get_server_info(ctx,pulse_server_cb,NULL)); pa_context_set_subscribe_callback(ctx,pulse_subscribe_cb,NULL); pa_operation_unref( pa_context_subscribe(ctx,PA_SUBSCRIPTION_MASK_SERVER | PA_SUBSCRIPTION_MASK_SINK | PA_SUBSCRIPTION_MASK_SINK_INPUT | PA_SUBSCRIPTION_MASK_SOURCE | PA_SUBSCRIPTION_MASK_SOURCE_OUTPUT, NULL, NULL)); } } void sfwbar_module_init ( ModuleApiV1 *api ) { pa_glib_mainloop *ploop; sfwbar_module_api = api; ploop = pa_glib_mainloop_new(g_main_context_get_thread_default()); papi = pa_glib_mainloop_get_api(ploop); pctx = pa_context_new(papi,"sfwbar"); pa_context_connect(pctx, NULL, PA_CONTEXT_NOFAIL, NULL); pa_context_set_state_callback(pctx,pulse_state_cb,NULL); } void sfwbar_module_invalidate ( void ) { invalid = TRUE; } void *pulse_expr_func ( void **params, void *widget, void *event ) { gchar *cmd; pulse_info *info; if(!params || !params[0]) return g_strdup(""); if(!g_ascii_strncasecmp(params[0],"sink-",5)) { info = pulse_info_from_name(&sink_list,params[1]?params[1]:sink_name, FALSE); cmd = params[0]+5; } else if(!g_ascii_strncasecmp(params[0],"source-",7)) { info = pulse_info_from_name(&source_list,params[1]?params[1]:source_name, FALSE); cmd = params[0]+7; } else info = NULL; if(!info || !cmd || !*cmd) return g_strdup(""); if(!g_ascii_strcasecmp(cmd,"volume")) return g_strdup_printf("%f",info->vol); else if(!g_ascii_strcasecmp(cmd,"mute")) return g_strdup_printf("%d",info->mute); else if(!g_ascii_strcasecmp(cmd,"icon")) return g_strdup(info->icon?info->icon:""); else if(!g_ascii_strcasecmp(cmd,"form")) return g_strdup(info->form?info->form:""); else if(!g_ascii_strcasecmp(cmd,"port")) return g_strdup(info->port?info->port:""); else if(!g_ascii_strcasecmp(cmd,"monitor")) return g_strdup(info->monitor?info->monitor:""); return g_strdup_printf("invalid query: %s",cmd); } static pa_cvolume *pulse_adjust_volume ( pa_cvolume *vol, gchar *vstr ) { gint vdelta; if(!vstr) return vol; while(*vstr==' ') vstr++; vdelta = g_ascii_strtod(vstr,NULL)*PA_VOLUME_NORM/100; if(*vstr!='+' && *vstr!='-') vdelta -= pa_cvolume_avg(vol); if(vdelta > 0) pa_cvolume_inc_clamp(vol,vdelta, PA_VOLUME_UI_MAX); else pa_cvolume_dec(vol,-vdelta); return vol; } static gboolean pulse_mute_parse ( gchar *cmd, gboolean mute ) { while(*cmd==' ') cmd++; if(!g_ascii_strcasecmp(cmd,"toggle")) return !mute; else if(!g_ascii_strcasecmp(cmd,"true")) return TRUE; else if(!g_ascii_strcasecmp(cmd,"false")) return FALSE; return mute; } static void pulse_action ( gchar *cmd, gchar *name, void *d1, void *d2, void *d3, void *d4 ) { pulse_info *info; pa_operation *op; if(!g_ascii_strncasecmp(cmd,"sink-",5)) info = pulse_info_from_name(&sink_list,name?name:sink_name,FALSE); else if(!g_ascii_strncasecmp(cmd,"source-",7)) info = pulse_info_from_name(&source_list,name?name:source_name,FALSE); else info = NULL; if(!info) return; if(!g_ascii_strncasecmp(cmd,"sink-volume",11)) op = pa_context_set_sink_volume_by_index(pctx,info->idx, pulse_adjust_volume(&info->cvol,cmd+11),NULL,NULL); else if(!g_ascii_strncasecmp(cmd,"source-volume",13)) op = pa_context_set_source_volume_by_index(pctx,info->idx, pulse_adjust_volume(&info->cvol,cmd+13), NULL,NULL); else if(!g_ascii_strncasecmp(cmd,"sink-mute",9)) op = pa_context_set_sink_mute_by_index(pctx,info->idx, pulse_mute_parse(cmd+9,info->mute),NULL,NULL); else if(!g_ascii_strncasecmp(cmd,"source-mute",11)) op = pa_context_set_sink_mute_by_index(pctx,info->idx, pulse_mute_parse(cmd+11,info->mute),NULL,NULL); else op = NULL; if(op) pa_operation_unref(op); } static void pulse_set_sink_action ( gchar *sink, gchar *dummy, void *d1, void *d2, void *d3, void *d4 ) { pulse_set_sink(sink,TRUE); } static void pulse_set_source_action ( gchar *source, gchar *dummy, void *d1, void *d2, void *d3, void *d4 ) { pulse_set_source(source,TRUE); } ModuleExpressionHandlerV1 handler1 = { .flags = 0, .name = "Pulse", .parameters = "Ss", .function = pulse_expr_func }; ModuleExpressionHandlerV1 *sfwbar_expression_handlers[] = { &handler1, NULL }; ModuleActionHandlerV1 act_handler1 = { .name = "PulseCmd", .function = pulse_action }; ModuleActionHandlerV1 act_handler2 = { .name = "PulseSetDefaultSink", .function = pulse_set_sink_action }; ModuleActionHandlerV1 act_handler3 = { .name = "PulseSetDefaultSource", .function = pulse_set_source_action }; ModuleActionHandlerV1 *sfwbar_action_handlers[] = { &act_handler1, &act_handler2, &act_handler3, NULL }; sfwbar-1.0~beta13/modules/xkbmap.c000066400000000000000000000034071450657570000171470ustar00rootroot00000000000000/* This entire file is licensed under GNU General Public License v3.0 * * Copyright 2023- Sfwbar maintainers */ #include #include #include "../src/module.h" static ModuleApiV1 *sfwbar_module_api; gint64 sfwbar_module_signature = 0x73f4d956a1; guint16 sfwbar_module_version = 1; static struct rxkb_context *ctx; void sfwbar_module_init ( ModuleApiV1 *api ) { sfwbar_module_api = api; ctx = rxkb_context_new (RXKB_CONTEXT_NO_DEFAULT_INCLUDES); rxkb_context_include_path_append_default(ctx); rxkb_context_parse_default_ruleset(ctx); } static const gchar *xkb_get_value ( struct rxkb_layout *layout, gchar *type ) { if(!g_ascii_strcasecmp(type,"description")) return rxkb_layout_get_description(layout); if(!g_ascii_strcasecmp(type,"name")) return rxkb_layout_get_name(layout); if(!g_ascii_strcasecmp(type,"variant")) return rxkb_layout_get_variant(layout); if(!g_ascii_strcasecmp(type,"brief")) return rxkb_layout_get_brief(layout); return g_strdup_printf("XkbMap: Invalid type: %s",type); } void *xkb_map_expr_func ( void **params, void *widget, void *event ) { struct rxkb_layout *layout; if(!params || !params[0] || !params[1] || !params[2]) return g_strdup("XkbMap: missing parameter(s)"); for(layout=rxkb_layout_first(ctx);layout;layout=rxkb_layout_next(layout)) { if(!g_strcmp0(params[0],xkb_get_value(layout,params[1]))) return g_strdup(xkb_get_value(layout,params[2])); } return g_strdup_printf("XkbMap: Unknown layout: %s",(gchar *)params[0]); } ModuleExpressionHandlerV1 handler1 = { .flags = MODULE_EXPR_DETERMINISTIC, .name = "XkbMap", .parameters = "SSS", .function = xkb_map_expr_func }; ModuleExpressionHandlerV1 *sfwbar_expression_handlers[] = { &handler1, NULL }; sfwbar-1.0~beta13/packaging/000077500000000000000000000000001450657570000157715ustar00rootroot00000000000000sfwbar-1.0~beta13/packaging/PKGBUILD000066400000000000000000000015511450657570000171170ustar00rootroot00000000000000# Maintainer: Lev Babiev pkgname=sfwbar pkgver=1.0_beta10 pkgrel=1 pkgdesc='S* Floating Window taskBar' arch=('x86_64') url='https://github.com/LBCrion/sfwbar' license=('GPL3') depends=( 'wayland' 'gtk3' 'json-c' 'gtk-layer-shell' 'wayland-protocols' ) optdepends=( 'libpulse: pulse audio volume control', 'libmpdclient: music player daemon control', 'libxkbcommon: xkb layout conversion support' ) makedepends=( 'meson' ) source=("$pkgname-$pkgver.tar.gz::$url/archive/refs/tags/v$pkgver.tar.gz") sha256sums=('88d192940c8f4c74fbeab6f054fa9ec57b42f7053859317c9a0de9cf6995b2a3') build() { cd "$pkgname-$pkgver" meson --prefix=/usr \ --buildtype=plain \ build ninja -C build } package() { cd "$pkgname-$pkgver" DESTDIR="$pkgdir" ninja -C build install } sfwbar-1.0~beta13/packaging/freebsd/000077500000000000000000000000001450657570000174035ustar00rootroot00000000000000sfwbar-1.0~beta13/packaging/freebsd/Makefile000066400000000000000000000027161450657570000210510ustar00rootroot00000000000000PORTNAME= sfwbar DISTVERSIONPREFIX= v DISTVERSION= 1.0_beta12 PORTREVISION= 1 CATEGORIES= x11 wayland MAINTAINER= tino.engel@mail.de COMMENT= Flexible taskbar application for wayland compositors WWW= https://github.com/LBCrion/sfwbar LICENSE= GPLv3 LICENSE_FILE= ${WRKSRC}/LICENSE BUILD_DEPENDS= wayland-protocols>0:graphics/wayland-protocols LIB_DEPENDS= libgtk-layer-shell.so:x11-toolkits/gtk-layer-shell \ libwayland-client.so:graphics/wayland \ libjson-c.so:devel/json-c USES= gnome meson pkgconfig USE_GNOME= cairo gdkpixbuf2 gtk30 USE_GITHUB= yes GH_ACCOUNT= LBCrion OPTIONS_DEFINE= MPD NETWORK PULSEAUDIO XKB ALSA BSD IDLE OPTIONS_DEFAULT= MPD NETWORK PULSEAUDIO XKB ALSA BSD IDLE OPTIONS_SUB= yes MPD_DESC= Music player daemon control MPD_LIB_DEPENDS= libmpdclient.so:audio/libmpdclient MPD_MESON_ENABLED= mpd NETWORK_DESC= Network interface monitor support NETWORK_MESON_ENABLED= network BSD_DESC= Support for BSD sysctl interface BSD_MESON_ENABLED= bsdctl IDLE_DESC= Support for idle inhibit protocol IDLE_MESON_ENABLED= idleinhibit PULSEAUDIO_DESC= Pulse audio volume control PULSEAUDIO_LIB_DEPENDS= libpulse.so:audio/pulseaudio PULSEAUDIO_MESON_ENABLED= pulse XKB_DESC= XkbCommon keyboard layout conversion support XKB_LIB_DEPENDS= libxkbregistry.so:x11/libxkbcommon XKB_MESON_ENABLED= xkb ALSA_DESC= ALSA audio volume control ALSA_LIB_DEPENDS= libasound.so:audio/alsa-lib ALSA_MESON_ENABLED= alsa SUB_FILES= pkg-message .include sfwbar-1.0~beta13/packaging/freebsd/distinfo000066400000000000000000000002771450657570000211530ustar00rootroot00000000000000TIMESTAMP = 1691478313 SHA256 (LBCrion-sfwbar-v1.0_beta12_GH0.tar.gz) = 98dfdc9915d57c17588e71a8c50964891e8aa6fcda8077ef390924fe28445eda SIZE (LBCrion-sfwbar-v1.0_beta12_GH0.tar.gz) = 362373 sfwbar-1.0~beta13/packaging/freebsd/files/000077500000000000000000000000001450657570000205055ustar00rootroot00000000000000sfwbar-1.0~beta13/packaging/freebsd/files/pkg-message.in000066400000000000000000000014541450657570000232440ustar00rootroot00000000000000[ { type: install message: < 1.0_beta12 - version bump * Thu May 11 2023 Lev Babiev 1.0_beta11 - version bump * Mon Mar 13 2023 Lev Babiev 1.0_beta10 - version bump - add module dependencies * Fri Nov 18 2022 Lev Babiev 1.0_beta9 - version bump * Wed Aug 03 2022 Lev Babiev 1.0_beta8 - version bump * Thu Apr 14 2022 Lev Babiev 1.0_beta5.1 - version bump * Sun Feb 6 2022 Lev Babiev 1.0_beta4 - version bump * Sat Jan 22 2022 Lev Babiev 1.0_beta3 - version bump * Thu Dec 23 2021 Lev Babiev 1.0_beta2 - version bump * Sat Dec 18 2021 Lev Babiev 1.0_beta1 - version bump * Sun Sep 19 2021 Lev Babiev 0.9.10.1 - version bump * Sun Sep 19 2021 Lev Babiev 0.9.10 - version bump * Sat Aug 21 2021 Lev Babiev 0.9.9.3-1 - version bump * Fri Jun 18 2021 Lev Babiev 0.9.8-1 - Fix SPEC * Tue Jun 08 2021 Adam Thiede 0.9.8-1 - Initial specfile sfwbar-1.0~beta13/protocols/000077500000000000000000000000001450657570000160715ustar00rootroot00000000000000sfwbar-1.0~beta13/protocols/wlr-foreign-toplevel-management-unstable-v1.xml000066400000000000000000000264221450657570000271550ustar00rootroot00000000000000 Copyright © 2018 Ilia Bozhinov Permission to use, copy, modify, distribute, and sell this software and its documentation for any purpose is hereby granted without fee, provided that the above copyright notice appear in all copies and that both that copyright notice and this permission notice appear in supporting documentation, and that the name of the copyright holders not be used in advertising or publicity pertaining to distribution of the software without specific, written prior permission. The copyright holders make no representations about the suitability of this software for any purpose. It is provided "as is" without express or implied warranty. THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. The purpose of this protocol is to enable the creation of taskbars and docks by providing them with a list of opened applications and letting them request certain actions on them, like maximizing, etc. After a client binds the zwlr_foreign_toplevel_manager_v1, each opened toplevel window will be sent via the toplevel event This event is emitted whenever a new toplevel window is created. It is emitted for all toplevels, regardless of the app that has created them. All initial details of the toplevel(title, app_id, states, etc.) will be sent immediately after this event via the corresponding events in zwlr_foreign_toplevel_handle_v1. Indicates the client no longer wishes to receive events for new toplevels. However the compositor may emit further toplevel_created events, until the finished event is emitted. The client must not send any more requests after this one. This event indicates that the compositor is done sending events to the zwlr_foreign_toplevel_manager_v1. The server will destroy the object immediately after sending this request, so it will become invalid and the client should free any resources associated with it. A zwlr_foreign_toplevel_handle_v1 object represents an opened toplevel window. Each app may have multiple opened toplevels. Each toplevel has a list of outputs it is visible on, conveyed to the client with the output_enter and output_leave events. This event is emitted whenever the title of the toplevel changes. This event is emitted whenever the app-id of the toplevel changes. This event is emitted whenever the toplevel becomes visible on the given output. A toplevel may be visible on multiple outputs. This event is emitted whenever the toplevel stops being visible on the given output. It is guaranteed that an entered-output event with the same output has been emitted before this event. Requests that the toplevel be maximized. If the maximized state actually changes, this will be indicated by the state event. Requests that the toplevel be unmaximized. If the maximized state actually changes, this will be indicated by the state event. Requests that the toplevel be minimized. If the minimized state actually changes, this will be indicated by the state event. Requests that the toplevel be unminimized. If the minimized state actually changes, this will be indicated by the state event. Request that this toplevel be activated on the given seat. There is no guarantee the toplevel will be actually activated. The different states that a toplevel can have. These have the same meaning as the states with the same names defined in xdg-toplevel This event is emitted immediately after the zlw_foreign_toplevel_handle_v1 is created and each time the toplevel state changes, either because of a compositor action or because of a request in this protocol. This event is sent after all changes in the toplevel state have been sent. This allows changes to the zwlr_foreign_toplevel_handle_v1 properties to be seen as atomic, even if they happen via multiple events. Send a request to the toplevel to close itself. The compositor would typically use a shell-specific method to carry out this request, for example by sending the xdg_toplevel.close event. However, this gives no guarantees the toplevel will actually be destroyed. If and when this happens, the zwlr_foreign_toplevel_handle_v1.closed event will be emitted. The rectangle of the surface specified in this request corresponds to the place where the app using this protocol represents the given toplevel. It can be used by the compositor as a hint for some operations, e.g minimizing. The client is however not required to set this, in which case the compositor is free to decide some default value. If the client specifies more than one rectangle, only the last one is considered. The dimensions are given in surface-local coordinates. Setting width=height=0 removes the already-set rectangle. This event means the toplevel has been destroyed. It is guaranteed there won't be any more events for this zwlr_foreign_toplevel_handle_v1. The toplevel itself becomes inert so any requests will be ignored except the destroy request. Destroys the zwlr_foreign_toplevel_handle_v1 object. This request should be called either when the client does not want to use the toplevel anymore or after the closed event to finalize the destruction of the object. Requests that the toplevel be fullscreened on the given output. If the fullscreen state and/or the outputs the toplevel is visible on actually change, this will be indicated by the state and output_enter/leave events. The output parameter is only a hint to the compositor. Also, if output is NULL, the compositor should decide which output the toplevel will be fullscreened on, if at all. Requests that the toplevel be unfullscreened. If the fullscreen state actually changes, this will be indicated by the state event. This event is emitted whenever the parent of the toplevel changes. No event is emitted when the parent handle is destroyed by the client. sfwbar-1.0~beta13/protocols/wlr-layer-shell-unstable-v1.xml000066400000000000000000000440361450657570000240040ustar00rootroot00000000000000 Copyright © 2017 Drew DeVault Permission to use, copy, modify, distribute, and sell this software and its documentation for any purpose is hereby granted without fee, provided that the above copyright notice appear in all copies and that both that copyright notice and this permission notice appear in supporting documentation, and that the name of the copyright holders not be used in advertising or publicity pertaining to distribution of the software without specific, written prior permission. The copyright holders make no representations about the suitability of this software for any purpose. It is provided "as is" without express or implied warranty. THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. Clients can use this interface to assign the surface_layer role to wl_surfaces. Such surfaces are assigned to a "layer" of the output and rendered with a defined z-depth respective to each other. They may also be anchored to the edges and corners of a screen and specify input handling semantics. This interface should be suitable for the implementation of many desktop shell components, and a broad number of other applications that interact with the desktop. Create a layer surface for an existing surface. This assigns the role of layer_surface, or raises a protocol error if another role is already assigned. Creating a layer surface from a wl_surface which has a buffer attached or committed is a client error, and any attempts by a client to attach or manipulate a buffer prior to the first layer_surface.configure call must also be treated as errors. After creating a layer_surface object and setting it up, the client must perform an initial commit without any buffer attached. The compositor will reply with a layer_surface.configure event. The client must acknowledge it and is then allowed to attach a buffer to map the surface. You may pass NULL for output to allow the compositor to decide which output to use. Generally this will be the one that the user most recently interacted with. Clients can specify a namespace that defines the purpose of the layer surface. These values indicate which layers a surface can be rendered in. They are ordered by z depth, bottom-most first. Traditional shell surfaces will typically be rendered between the bottom and top layers. Fullscreen shell surfaces are typically rendered at the top layer. Multiple surfaces can share a single layer, and ordering within a single layer is undefined. This request indicates that the client will not use the layer_shell object any more. Objects that have been created through this instance are not affected. An interface that may be implemented by a wl_surface, for surfaces that are designed to be rendered as a layer of a stacked desktop-like environment. Layer surface state (layer, size, anchor, exclusive zone, margin, interactivity) is double-buffered, and will be applied at the time wl_surface.commit of the corresponding wl_surface is called. Attaching a null buffer to a layer surface unmaps it. Unmapping a layer_surface means that the surface cannot be shown by the compositor until it is explicitly mapped again. The layer_surface returns to the state it had right after layer_shell.get_layer_surface. The client can re-map the surface by performing a commit without any buffer attached, waiting for a configure event and handling it as usual. Sets the size of the surface in surface-local coordinates. The compositor will display the surface centered with respect to its anchors. If you pass 0 for either value, the compositor will assign it and inform you of the assignment in the configure event. You must set your anchor to opposite edges in the dimensions you omit; not doing so is a protocol error. Both values are 0 by default. Size is double-buffered, see wl_surface.commit. Requests that the compositor anchor the surface to the specified edges and corners. If two orthogonal edges are specified (e.g. 'top' and 'left'), then the anchor point will be the intersection of the edges (e.g. the top left corner of the output); otherwise the anchor point will be centered on that edge, or in the center if none is specified. Anchor is double-buffered, see wl_surface.commit. Requests that the compositor avoids occluding an area with other surfaces. The compositor's use of this information is implementation-dependent - do not assume that this region will not actually be occluded. A positive value is only meaningful if the surface is anchored to one edge or an edge and both perpendicular edges. If the surface is not anchored, anchored to only two perpendicular edges (a corner), anchored to only two parallel edges or anchored to all edges, a positive value will be treated the same as zero. A positive zone is the distance from the edge in surface-local coordinates to consider exclusive. Surfaces that do not wish to have an exclusive zone may instead specify how they should interact with surfaces that do. If set to zero, the surface indicates that it would like to be moved to avoid occluding surfaces with a positive exclusive zone. If set to -1, the surface indicates that it would not like to be moved to accommodate for other surfaces, and the compositor should extend it all the way to the edges it is anchored to. For example, a panel might set its exclusive zone to 10, so that maximized shell surfaces are not shown on top of it. A notification might set its exclusive zone to 0, so that it is moved to avoid occluding the panel, but shell surfaces are shown underneath it. A wallpaper or lock screen might set their exclusive zone to -1, so that they stretch below or over the panel. The default value is 0. Exclusive zone is double-buffered, see wl_surface.commit. Requests that the surface be placed some distance away from the anchor point on the output, in surface-local coordinates. Setting this value for edges you are not anchored to has no effect. The exclusive zone includes the margin. Margin is double-buffered, see wl_surface.commit. Types of keyboard interaction possible for layer shell surfaces. The rationale for this is twofold: (1) some applications are not interested in keyboard events and not allowing them to be focused can improve the desktop experience; (2) some applications will want to take exclusive keyboard focus. This value indicates that this surface is not interested in keyboard events and the compositor should never assign it the keyboard focus. This is the default value, set for newly created layer shell surfaces. This is useful for e.g. desktop widgets that display information or only have interaction with non-keyboard input devices. Request exclusive keyboard focus if this surface is above the shell surface layer. For the top and overlay layers, the seat will always give exclusive keyboard focus to the top-most layer which has keyboard interactivity set to exclusive. If this layer contains multiple surfaces with keyboard interactivity set to exclusive, the compositor determines the one receiving keyboard events in an implementation- defined manner. In this case, no guarantee is made when this surface will receive keyboard focus (if ever). For the bottom and background layers, the compositor is allowed to use normal focus semantics. This setting is mainly intended for applications that need to ensure they receive all keyboard events, such as a lock screen or a password prompt. This requests the compositor to allow this surface to be focused and unfocused by the user in an implementation-defined manner. The user should be able to unfocus this surface even regardless of the layer it is on. Typically, the compositor will want to use its normal mechanism to manage keyboard focus between layer shell surfaces with this setting and regular toplevels on the desktop layer (e.g. click to focus). Nevertheless, it is possible for a compositor to require a special interaction to focus or unfocus layer shell surfaces (e.g. requiring a click even if focus follows the mouse normally, or providing a keybinding to switch focus between layers). This setting is mainly intended for desktop shell components (e.g. panels) that allow keyboard interaction. Using this option can allow implementing a desktop shell that can be fully usable without the mouse. Set how keyboard events are delivered to this surface. By default, layer shell surfaces do not receive keyboard events; this request can be used to change this. This setting is inherited by child surfaces set by the get_popup request. Layer surfaces receive pointer, touch, and tablet events normally. If you do not want to receive them, set the input region on your surface to an empty region. Keyboard interactivity is double-buffered, see wl_surface.commit. This assigns an xdg_popup's parent to this layer_surface. This popup should have been created via xdg_surface::get_popup with the parent set to NULL, and this request must be invoked before committing the popup's initial state. See the documentation of xdg_popup for more details about what an xdg_popup is and how it is used. When a configure event is received, if a client commits the surface in response to the configure event, then the client must make an ack_configure request sometime before the commit request, passing along the serial of the configure event. If the client receives multiple configure events before it can respond to one, it only has to ack the last configure event. A client is not required to commit immediately after sending an ack_configure request - it may even ack_configure several times before its next surface commit. A client may send multiple ack_configure requests before committing, but only the last request sent before a commit indicates which configure event the client really is responding to. This request destroys the layer surface. The configure event asks the client to resize its surface. Clients should arrange their surface for the new states, and then send an ack_configure request with the serial sent in this configure event at some point before committing the new surface. The client is free to dismiss all but the last configure event it received. The width and height arguments specify the size of the window in surface-local coordinates. The size is a hint, in the sense that the client is free to ignore it if it doesn't resize, pick a smaller size (to satisfy aspect ratio or resize in steps of NxM pixels). If the client picks a smaller size and is anchored to two opposite anchors (e.g. 'top' and 'bottom'), the surface will be centered on this axis. If the width or height arguments are zero, it means the client should decide its own window dimension. The closed event is sent by the compositor when the surface will no longer be shown. The output may have been destroyed or the user may have asked for it to be removed. Further changes to the surface will be ignored. The client should destroy the resource after receiving this event, and create a new surface if they so choose. Change the layer that the surface is rendered on. Layer is double-buffered, see wl_surface.commit. sfwbar-1.0~beta13/protocols/xdg-output-unstable-v1.xml000066400000000000000000000225121450657570000230740ustar00rootroot00000000000000 Copyright © 2017 Red Hat Inc. 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 (including the next paragraph) 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. This protocol aims at describing outputs in a way which is more in line with the concept of an output on desktop oriented systems. Some information are more specific to the concept of an output for a desktop oriented system and may not make sense in other applications, such as IVI systems for example. Typically, the global compositor space on a desktop system is made of a contiguous or overlapping set of rectangular regions. Some of the information provided in this protocol might be identical to their counterparts already available from wl_output, in which case the information provided by this protocol should be preferred to their equivalent in wl_output. The goal is to move the desktop specific concepts (such as output location within the global compositor space, the connector name and types, etc.) out of the core wl_output protocol. Warning! The protocol described in this file is experimental and backward incompatible changes may be made. Backward compatible changes may be added together with the corresponding interface version bump. Backward incompatible changes are done by bumping the version number in the protocol and interface names and resetting the interface version. Once the protocol is to be declared stable, the 'z' prefix and the version number in the protocol and interface names are removed and the interface version number is reset. A global factory interface for xdg_output objects. Using this request a client can tell the server that it is not going to use the xdg_output_manager object anymore. Any objects already created through this instance are not affected. This creates a new xdg_output object for the given wl_output. An xdg_output describes part of the compositor geometry. This typically corresponds to a monitor that displays part of the compositor space. For objects version 3 onwards, after all xdg_output properties have been sent (when the object is created and when properties are updated), a wl_output.done event is sent. This allows changes to the output properties to be seen as atomic, even if they happen via multiple events. Using this request a client can tell the server that it is not going to use the xdg_output object anymore. The position event describes the location of the wl_output within the global compositor space. The logical_position event is sent after creating an xdg_output (see xdg_output_manager.get_xdg_output) and whenever the location of the output changes within the global compositor space. The logical_size event describes the size of the output in the global compositor space. For example, a surface without any buffer scale, transformation nor rotation set, with the size matching the logical_size will have the same size as the corresponding output when displayed. Most regular Wayland clients should not pay attention to the logical size and would rather rely on xdg_shell interfaces. Some clients such as Xwayland, however, need this to configure their surfaces in the global compositor space as the compositor may apply a different scale from what is advertised by the output scaling property (to achieve fractional scaling, for example). For example, for a wl_output mode 3840×2160 and a scale factor 2: - A compositor not scaling the surface buffers will advertise a logical size of 3840×2160, - A compositor automatically scaling the surface buffers will advertise a logical size of 1920×1080, - A compositor using a fractional scale of 1.5 will advertise a logical size to 2560×1620. For example, for a wl_output mode 1920×1080 and a 90 degree rotation, the compositor will advertise a logical size of 1080x1920. The logical_size event is sent after creating an xdg_output (see xdg_output_manager.get_xdg_output) and whenever the logical size of the output changes, either as a result of a change in the applied scale or because of a change in the corresponding output mode(see wl_output.mode) or transform (see wl_output.transform). This event is sent after all other properties of an xdg_output have been sent. This allows changes to the xdg_output properties to be seen as atomic, even if they happen via multiple events. For objects version 3 onwards, this event is deprecated. Compositors are not required to send it anymore and must send wl_output.done instead. Many compositors will assign names to their outputs, show them to the user, allow them to be configured by name, etc. The client may wish to know this name as well to offer the user similar behaviors. The naming convention is compositor defined, but limited to alphanumeric characters and dashes (-). Each name is unique among all wl_output globals, but if a wl_output global is destroyed the same name may be reused later. The names will also remain consistent across sessions with the same hardware and software configuration. Examples of names include 'HDMI-A-1', 'WL-1', 'X11-1', etc. However, do not assume that the name is a reflection of an underlying DRM connector, X11 connection, etc. The name event is sent after creating an xdg_output (see xdg_output_manager.get_xdg_output). This event is only sent once per xdg_output, and the name does not change over the lifetime of the wl_output global. Many compositors can produce human-readable descriptions of their outputs. The client may wish to know this description as well, to communicate the user for various purposes. The description is a UTF-8 string with no convention defined for its contents. Examples might include 'Foocorp 11" Display' or 'Virtual X11 output via :1'. The description event is sent after creating an xdg_output (see xdg_output_manager.get_xdg_output) and whenever the description changes. The description is optional, and may not be sent at all. For objects of version 2 and lower, this event is only sent once per xdg_output, and the description does not change over the lifetime of the wl_output global. sfwbar-1.0~beta13/src/000077500000000000000000000000001450657570000146345ustar00rootroot00000000000000sfwbar-1.0~beta13/src/action.c000066400000000000000000000122161450657570000162570ustar00rootroot00000000000000/* This entire file is licensed under GNU General Public License v3.0 * * Copyright 2022- sfwbar maintainers */ #include "expr.h" #include "basewidget.h" #include "taskbaritem.h" #include "action.h" #include "module.h" static GHashTable *functions; static GHashTable *trigger_actions; void action_function_add ( gchar *name, GList *actions ) { GList *list; if(!functions) functions = g_hash_table_new((GHashFunc)str_nhash,(GEqualFunc)str_nequal); list = g_hash_table_lookup(functions,name); if(list) { list = g_list_concat(list,actions); g_hash_table_replace(functions,name,list); g_free(name); } else g_hash_table_insert(functions, name, actions); } void action_function_exec ( gchar *name, GtkWidget *w, GdkEvent *ev, window_t *win, guint16 *state ) { GList *iter; window_t *stat_win; if(!name || !functions) return; if(win) stat_win = g_memdup2(win,sizeof(window_t)); else stat_win = NULL; for(iter=g_hash_table_lookup(functions, name); iter; iter=g_list_next(iter)) action_exec(w,iter->data,ev,stat_win,state); g_free(stat_win); } guint16 action_state_build ( GtkWidget *widget, window_t *win ) { guint16 state = 0; if(win) { state = win->state; if(wintree_is_focused(win->uid)) state |= WS_FOCUSED; } if(widget && IS_BASE_WIDGET(widget)) state |= base_widget_get_state(widget); return state; } void action_exec ( GtkWidget *widget, action_t *action, GdkEvent *event, window_t *win, guint16 *istate ) { ModuleActionHandlerV1 *ahandler; guint16 state; GList *children, *iter; action_t *caction; ExprCache *addr; if(!action) return; ahandler = module_action_get(action->quark); if(!ahandler) return; if(ahandler->flags & MODULE_ACT_ADDRESS_ONLY) addr = action->command; else addr = action->addr; expr_cache_eval(addr); if(addr->cache && ahandler->flags & MODULE_ACT_WIDGET_ADDRESS ) { widget = base_widget_from_id(addr->cache); if(IS_TASKBAR_ITEM(widget)) win = flow_item_get_parent(widget); event = NULL; istate = NULL; } state = istate? *istate : action_state_build ( widget, win ); if(action->cond & WS_CHILDREN) state |= WS_CHILDREN; if(((action->cond & 0x0f) || (action->ncond & 0x0f)) && !win) return; if(((action->cond & 0xf0) || (action->ncond & 0xf0)) && !widget ) return; if((state & action->cond) != action->cond) return; if((~state & action->ncond) != action->ncond) return; if( !(ahandler->flags & MODULE_ACT_CMD_BY_DEF) && !(ahandler->flags & MODULE_ACT_ADDRESS_ONLY) ) { action->command->widget = widget; action->command->event = event; expr_cache_eval(action->command); action->command->widget = NULL; action->command->event = NULL; } g_debug("action: %s '%s', '%s', widget=%p, win=%d from '%s', '%s'", ahandler->name, action->addr->cache,action->command->cache,widget, win?GPOINTER_TO_INT(win->uid):0, action->addr->definition, action->command->definition); if(action->cond & WS_CHILDREN && GTK_IS_CONTAINER(base_widget_get_child(widget))) { caction = action_dup(action); caction->cond = 0; caction->ncond = 0; children = gtk_container_get_children( GTK_CONTAINER(base_widget_get_child(widget))); for(iter=children;iter;iter=g_list_next(iter)) action_exec(iter->data,caction,event, IS_TASKBAR_ITEM(iter->data)?flow_item_get_parent(iter->data):win, NULL); g_list_free(children); action_free(caction,NULL); return; } module_action_exec(action->quark, (ahandler->flags & MODULE_ACT_CMD_BY_DEF)?action->command->definition: action->command->cache, action->addr->cache, widget, event, win, &state); } action_t *action_new ( void ) { action_t *new; new = g_malloc0(sizeof(action_t)); new->command = expr_cache_new(); new->addr = expr_cache_new(); return new; } action_t *action_dup ( action_t *src ) { action_t *dest; if(!src) return NULL; dest = action_new(); dest->cond = src->cond; dest->ncond = src->ncond; dest->quark = src->quark; dest->command->definition = g_strdup(src->command->definition); dest->command->cache = g_strdup(src->command->cache); dest->command->eval = src->command->eval; dest->addr->definition = g_strdup(src->addr->definition); dest->addr->cache = g_strdup(src->addr->cache); dest->addr->eval = src->addr->eval; return dest; } void action_free ( action_t *action, GObject *old ) { if(!action) return; expr_cache_free(action->command); expr_cache_free(action->addr); g_free(action); } void action_trigger_add ( action_t *action, gchar *trigger ) { void *old; if(!trigger_actions) trigger_actions = g_hash_table_new((GHashFunc)str_nhash,(GEqualFunc)str_nequal); old = g_hash_table_lookup(trigger_actions,trigger); if(old) { g_message("Action for trigger '%s' is already defined",trigger); g_free(trigger); action_free(action,NULL); return; } g_hash_table_insert(trigger_actions, trigger, action); } action_t *action_trigger_lookup ( gchar *trigger ) { if(!trigger_actions) return NULL; return g_hash_table_lookup(trigger_actions,trigger); } sfwbar-1.0~beta13/src/action.h000066400000000000000000000013041450657570000162600ustar00rootroot00000000000000#ifndef __ACTION_H__ #define __ACTION_H__ #include "wintree.h" #include "expr.h" typedef struct user_action { guchar cond; guchar ncond; gint mod; ExprCache *command; ExprCache *addr; GQuark quark; } action_t; void action_exec ( GtkWidget *, action_t *, GdkEvent *, window_t *, guint16 *); action_t *action_new ( void ); void action_free ( action_t *, GObject *); action_t *action_dup ( action_t *src ); void action_function_add ( gchar *, GList *); void action_function_exec ( gchar *, GtkWidget *, GdkEvent *, window_t *, guint16 *); void action_trigger_add ( action_t *action, gchar *trigger ); action_t *action_trigger_lookup ( gchar *trigger ); void action_lib_init ( void ); #endif sfwbar-1.0~beta13/src/actionlib.c000066400000000000000000000303061450657570000167460ustar00rootroot00000000000000/* This entire file is licensed under GNU General Public License v3.0 * * Copyright 2023- sfwbar maintainers */ #include "bar.h" #include "basewidget.h" #include "config.h" #include "action.h" #include "menu.h" #include "sway_ipc.h" #include "switcher.h" #include "module.h" #include "popup.h" #include "client.h" static void exec_action ( gchar *cmd, gchar *name, void *widget, void *event, void *win, void *state ) { gint argc; gchar **argv; if(!cmd || !g_shell_parse_argv(cmd,&argc,&argv,NULL)) return; g_spawn_async(NULL,argv,NULL,G_SPAWN_SEARCH_PATH | G_SPAWN_STDOUT_TO_DEV_NULL | G_SPAWN_STDERR_TO_DEV_NULL,NULL,NULL,NULL,NULL); g_strfreev(argv); } static ModuleActionHandlerV1 exec_handler = { .name = "Exec", .function = (ModuleActionFunc)exec_action }; static void function_action ( gchar *cmd, gchar *name, void *widget, void *event, void *win, void *state ) { action_function_exec(cmd,widget,event,win,state); } static ModuleActionHandlerV1 function_handler = { .name = "Function", .flags = MODULE_ACT_WIDGET_ADDRESS, .function = (ModuleActionFunc)function_action }; static void piperead_action ( gchar *cmd, gchar *name, void *widget, void *event, void *win, void *state ) { config_pipe_read(cmd); } static ModuleActionHandlerV1 piperead_handler = { .name = "PipeRead", .function = piperead_action }; static void menuclear_action ( gchar *cmd, gchar *name, void *widget, void *event, void *win, void *state ) { menu_remove(cmd); } static ModuleActionHandlerV1 menuclear_handler = { .name = "MenuClear", .function = menuclear_action }; static void menu_action ( gchar *cmd, gchar *name, void *widget, void *event, window_t *win, guint16 *state ) { menu_popup(widget, menu_from_name(cmd), event, win?win->uid:NULL, state); } static ModuleActionHandlerV1 menu_handler = { .name = "Menu", .flags = MODULE_ACT_WIDGET_ADDRESS, .function = (ModuleActionFunc)menu_action }; static void swaycmd_action ( gchar *cmd, gchar *name, void *widget, void *event, window_t *win, guint16 *state ) { sway_ipc_command("%s",cmd); } static ModuleActionHandlerV1 swaycmd_handler = { .name = "SwayCmd", .function = (ModuleActionFunc)swaycmd_action }; static void swaywincmd_action ( gchar *cmd, gchar *name, void *widget, void *event, window_t *win, guint16 *state ) { if(win) sway_ipc_command("[con_id=%ld] %s",GPOINTER_TO_INT(win->uid), cmd); } static ModuleActionHandlerV1 swaywincmd_handler = { .name = "SwayWinCmd", .function = (ModuleActionFunc)swaywincmd_action }; static void mpdcmd_action ( gchar *cmd, gchar *name, void *widget, void *event, window_t *win, guint16 *state ) { client_mpd_command(cmd); } static ModuleActionHandlerV1 mpdcmd_handler = { .name = "MpdCmd", .function = (ModuleActionFunc)mpdcmd_action }; static void config_action_handler ( gchar *cmd, gchar *name, void *widget, void *event, window_t *win, guint16 *state ) { config_string(cmd); } static ModuleActionHandlerV1 config_handler = { .name = "Config", .function = (ModuleActionFunc)config_action_handler }; static void setmonitor_action ( gchar *cmd, gchar *name, void *widget, void *event, window_t *win, guint16 *state ) { bar_set_monitor(bar_from_name(name), cmd); } static ModuleActionHandlerV1 setmonitor_handler = { .name = "SetMonitor", .function = (ModuleActionFunc)setmonitor_action }; static void setlayer_action ( gchar *cmd, gchar *name, void *widget, void *event, window_t *win, guint16 *state ) { bar_set_layer(bar_from_name(name), cmd); } static ModuleActionHandlerV1 setlayer_handler = { .name = "SetLayer", .function = (ModuleActionFunc)setlayer_action }; static void setmirror_action ( gchar *cmd, gchar *name, void *widget, void *event, window_t *win, guint16 *state ) { bar_set_mirrors(bar_from_name(name), cmd); } static ModuleActionHandlerV1 setmirror_handler = { .name = "SetMirror", .function = (ModuleActionFunc)setmirror_action }; static void blockmirror_action ( gchar *cmd, gchar *name, void *widget, void *event, window_t *win, guint16 *state ) { bar_set_mirror_blocks(bar_from_name(name), cmd); } static ModuleActionHandlerV1 blockmirror_handler = { .name = "BlockMirror", .function = (ModuleActionFunc)blockmirror_action }; static void setbarsize_action ( gchar *cmd, gchar *name, void *widget, void *event, window_t *win, guint16 *state ) { bar_set_size(bar_from_name(name), cmd); } static ModuleActionHandlerV1 setbarsize_handler = { .name = "SetBarSize", .function = (ModuleActionFunc)setbarsize_action }; static void setbarid_action ( gchar *cmd, gchar *name, void *widget, void *event, window_t *win, guint16 *state ) { bar_set_id(bar_from_name(name), cmd); } static ModuleActionHandlerV1 setbarid_handler = { .name = "SetBarID", .function = (ModuleActionFunc)setbarid_action }; static void setbarsensor_action ( gchar *cmd, gchar *name, void *widget, void *event, window_t *win, guint16 *state ) { bar_set_sensor(bar_from_name(name), cmd); } static ModuleActionHandlerV1 setbarsensor_handler = { .name = "SetBarSensor", .function = (ModuleActionFunc)setbarsensor_action }; static void setexclusivezone_action ( gchar *cmd, gchar *name, void *widget, void *event, window_t *win, guint16 *state ) { bar_set_exclusive_zone(bar_from_name(name), cmd); } static ModuleActionHandlerV1 setexclusivezone_handler = { .name = "SetExclusiveZone", .function = (ModuleActionFunc)setexclusivezone_action }; static void setbarvisibility_action ( gchar *cmd, gchar *name, void *widget, void *event, window_t *win, guint16 *state ) { if(cmd) bar_set_visibility(bar_from_name(name), NULL, *cmd); } static ModuleActionHandlerV1 setbarvisibility_handler = { .name = "SetBarVisibility", .function = (ModuleActionFunc)setbarvisibility_action }; static void setvalue_action ( gchar *cmd, gchar *name, void *widget, void *event, window_t *win, guint16 *state ) { if(widget && cmd) base_widget_set_value(widget,g_strdup(cmd)); } static ModuleActionHandlerV1 setvalue_handler = { .name = "SetValue", .flags = MODULE_ACT_CMD_BY_DEF | MODULE_ACT_WIDGET_ADDRESS, .function = (ModuleActionFunc)setvalue_action }; static void setstyle_action ( gchar *cmd, gchar *name, void *widget, void *event, window_t *win, guint16 *state ) { if(widget && cmd) base_widget_set_style(widget,g_strdup(cmd)); } static ModuleActionHandlerV1 setstyle_handler = { .name = "SetStyle", .flags = MODULE_ACT_CMD_BY_DEF | MODULE_ACT_WIDGET_ADDRESS, .function = (ModuleActionFunc)setstyle_action }; static void settooltip_action ( gchar *cmd, gchar *name, void *widget, void *event, window_t *win, guint16 *state ) { if(widget && cmd) base_widget_set_tooltip(widget,g_strdup(cmd)); } static ModuleActionHandlerV1 settooltip_handler = { .name = "SetTooltip", .flags = MODULE_ACT_CMD_BY_DEF | MODULE_ACT_WIDGET_ADDRESS, .function = (ModuleActionFunc)settooltip_action }; static void userstate_action ( gchar *value, gchar *name, void *widget, void *event, window_t *win, guint16 *st ) { gchar *state; guint16 mask; if(!widget || !value) return; state = strchr(value,':'); if(!state) { state = value; mask = WS_USERSTATE; } else { state++; if(g_ascii_digit_value(*value)==2) mask = WS_USERSTATE2; else mask = WS_USERSTATE; } if(!g_ascii_strcasecmp(state,"on")) base_widget_set_state(widget,mask,TRUE); else base_widget_set_state(widget,mask,FALSE); } static ModuleActionHandlerV1 userstate_handler = { .name = "UserState", .flags = MODULE_ACT_WIDGET_ADDRESS, .function = (ModuleActionFunc)userstate_action }; static void popup_action ( gchar *cmd, gchar *name, void *widget, void *event, window_t *win, guint16 *state ) { popup_trigger(widget, cmd, event); } static ModuleActionHandlerV1 popup_handler = { .name = "PopUp", .flags = MODULE_ACT_WIDGET_ADDRESS, .function = (ModuleActionFunc)popup_action }; static void clientsend_action ( gchar *cmd, gchar *name, void *widget, void *event, window_t *win, guint16 *state ) { client_send(name, cmd); } static ModuleActionHandlerV1 clientsend_handler = { .name = "ClientSend", .function = (ModuleActionFunc)clientsend_action }; static void focus_action ( gchar *cmd, gchar *name, void *widget, void *event, window_t *win, guint16 *state ) { if(win) wintree_focus(win->uid); } static ModuleActionHandlerV1 focus_handler = { .name = "Focus", .flags = MODULE_ACT_WIDGET_ADDRESS | MODULE_ACT_ADDRESS_ONLY, .function = (ModuleActionFunc)focus_action }; static void close_action ( gchar *cmd, gchar *name, void *widget, void *event, window_t *win, guint16 *state ) { if(win) wintree_close(win->uid); } static ModuleActionHandlerV1 close_handler = { .name = "Close", .flags = MODULE_ACT_WIDGET_ADDRESS | MODULE_ACT_ADDRESS_ONLY, .function = (ModuleActionFunc)close_action }; static void minimize_action ( gchar *cmd, gchar *name, void *widget, void *event, window_t *win, guint16 *state ) { if(win) wintree_minimize(win->uid); } static ModuleActionHandlerV1 minimize_handler = { .name = "Minimize", .flags = MODULE_ACT_WIDGET_ADDRESS | MODULE_ACT_ADDRESS_ONLY, .function = (ModuleActionFunc)minimize_action }; static void maximize_action ( gchar *cmd, gchar *name, void *widget, void *event, window_t *win, guint16 *state ) { if(win) wintree_maximize(win->uid); } static ModuleActionHandlerV1 maximize_handler = { .name = "Maximize", .flags = MODULE_ACT_WIDGET_ADDRESS | MODULE_ACT_ADDRESS_ONLY, .function = (ModuleActionFunc)maximize_action }; static void unminimize_action ( gchar *cmd, gchar *name, void *widget, void *event, window_t *win, guint16 *state ) { if(win) wintree_unminimize(win->uid); } static ModuleActionHandlerV1 unminimize_handler = { .name = "UnMinimize", .flags = MODULE_ACT_WIDGET_ADDRESS | MODULE_ACT_ADDRESS_ONLY, .function = (ModuleActionFunc)unminimize_action }; static void unmaximize_action ( gchar *cmd, gchar *name, void *widget, void *event, window_t *win, guint16 *state ) { if(win) wintree_unmaximize(win->uid); } static ModuleActionHandlerV1 unmaximize_handler = { .name = "UnMaximize", .flags = MODULE_ACT_WIDGET_ADDRESS | MODULE_ACT_ADDRESS_ONLY, .function = (ModuleActionFunc)unmaximize_action }; static void eval_action ( gchar *cmd, gchar *name, void *widget, void *event, window_t *win, guint16 *state ) { gchar *val; if(!cmd || !name) return; val = config_value_string(g_strdup(""), cmd); scanner_var_new(name, NULL, val, G_TOKEN_SET, G_TOKEN_FIRST); g_free(val); } static ModuleActionHandlerV1 eval_handler = { .name = "Eval", .function = (ModuleActionFunc)eval_action }; static void switcher_action ( gchar *cmd, gchar *name, void *widget, void *event, window_t *win, guint16 *state ) { if(!cmd || !g_strcasecmp(cmd,"forward")) switcher_event(NULL); if(cmd && !g_strcasecmp(cmd,"back")) switcher_event((void *)1); } static ModuleActionHandlerV1 switcher_handler = { .name = "SwitcherEvent", .function = (ModuleActionFunc)switcher_action }; static void clear_widget_action ( gchar *cmd, gchar *name, void *widget, void *event, window_t *win, guint16 *state ) { GtkWidget *w; if(!cmd) return; w = base_widget_from_id(cmd); if(w) gtk_widget_destroy(w); } static ModuleActionHandlerV1 clear_widget_handler = { .name = "ClearWidget", .function = (ModuleActionFunc)clear_widget_action }; ModuleActionHandlerV1 *action_handlers[] = { &exec_handler, &function_handler, &piperead_handler, &menuclear_handler, &menu_handler, &swaycmd_handler, &swaywincmd_handler, &mpdcmd_handler, &config_handler, &setmonitor_handler, &setlayer_handler, &setmirror_handler, &blockmirror_handler, &setbarsize_handler, &setbarid_handler, &setexclusivezone_handler, &setbarsensor_handler, &setbarvisibility_handler, &setvalue_handler, &setstyle_handler, &settooltip_handler, &userstate_handler, &popup_handler, &clientsend_handler, &focus_handler, &close_handler, &minimize_handler, &maximize_handler, &unminimize_handler, &unmaximize_handler, &eval_handler, &switcher_handler, &clear_widget_handler, NULL }; void action_lib_init ( void ) { module_actions_add(action_handlers,"action library"); } sfwbar-1.0~beta13/src/bar.c000066400000000000000000000520301450657570000155440ustar00rootroot00000000000000/* This entire file is licensed under GNU General Public License v3.0 * * Copyright 2022- sfwbar maintainers */ #include #include #include #include "sfwbar.h" #include "bar.h" #include "wayland.h" #include "grid.h" #include "config.h" #include "taskbar.h" #include "window.h" G_DEFINE_TYPE_WITH_CODE (Bar, bar, GTK_TYPE_WINDOW, G_ADD_PRIVATE (Bar)); static GHashTable *bar_list; static GList *mirrors; static gboolean bar_sensor_unblock_cb ( GtkWidget *self ) { BarPrivate *priv; g_return_val_if_fail(IS_BAR(self), FALSE); priv = bar_get_instance_private(BAR(self)); priv->sensor_block = FALSE; return FALSE; } static gboolean bar_sensor_hide ( GtkWidget *self ) { BarPrivate *priv; g_return_val_if_fail(IS_BAR(self),FALSE); priv = bar_get_instance_private(BAR(self)); if(gtk_bin_get_child(GTK_BIN(self))==priv->sensor) return FALSE; if(window_ref_check(self)) return TRUE; priv->sensor_block = TRUE; g_idle_add((GSourceFunc)bar_sensor_unblock_cb, self); css_add_class(self,"sensor"); gtk_container_remove(GTK_CONTAINER(self),gtk_bin_get_child(GTK_BIN(self))); gtk_container_add(GTK_CONTAINER(self),priv->sensor); priv->sensor_state = FALSE; priv->sensor_handle = 0; return FALSE; } static gboolean bar_leave_notify_event ( GtkWidget *self, GdkEventCrossing *event ) { BarPrivate *priv; g_return_val_if_fail(IS_BAR(self),FALSE); priv = bar_get_instance_private(BAR(self)); if(!priv->sensor_timeout || !priv->sensor_state || priv->sensor_block) return TRUE; if(!priv->sensor_handle) priv->sensor_handle = g_timeout_add(priv->sensor_timeout, (GSourceFunc)bar_sensor_hide, self); return TRUE; } static void bar_sensor_show_bar ( GtkWidget *self ) { BarPrivate *priv; g_return_if_fail(IS_BAR(self)); priv = bar_get_instance_private(BAR(self)); bar_sensor_cancel_hide(self); priv->sensor_state = TRUE; if(gtk_bin_get_child(GTK_BIN(self))!=priv->sensor) return; priv->sensor_block = TRUE; g_idle_add((GSourceFunc)bar_sensor_unblock_cb, self); css_remove_class(self,"sensor"); gtk_container_remove(GTK_CONTAINER(self),gtk_bin_get_child(GTK_BIN(self))); gtk_container_add(GTK_CONTAINER(self),priv->box); } static gboolean bar_enter_notify_event ( GtkWidget *self, GdkEventCrossing *event ) { BarPrivate *priv; g_return_val_if_fail(IS_BAR(self),FALSE); priv = bar_get_instance_private(BAR(self)); if(!priv->sensor_timeout || priv->sensor_block) return TRUE; bar_sensor_show_bar(self); return TRUE; } static void bar_destroy ( GtkWidget *self ) { BarPrivate *priv, *ppriv; priv = bar_get_instance_private(BAR(self)); if(priv->mirror_parent) { ppriv = bar_get_instance_private(BAR(priv->mirror_parent)); ppriv->mirror_children = g_list_remove(ppriv->mirror_children, self); priv->mirror_parent = NULL; } mirrors = g_list_remove(mirrors, self); g_clear_pointer(&priv->name,g_free); g_clear_pointer(&priv->output,g_free); g_clear_pointer(&priv->bar_id,g_free); g_clear_pointer(&priv->size,g_free); g_clear_pointer(&priv->ezone,g_free); g_clear_pointer(&priv->layer,g_free); g_clear_pointer(&priv->mirror_targets,g_strfreev); g_clear_pointer(&priv->mirror_blocks,g_strfreev); g_clear_pointer(&priv->sensor,gtk_widget_destroy); g_clear_pointer(&priv->box,gtk_widget_destroy); GTK_WIDGET_CLASS(bar_parent_class)->destroy(self); } static void bar_init ( Bar *self ) { } static void bar_class_init ( BarClass *kclass ) { GTK_WIDGET_CLASS(kclass)->destroy = bar_destroy; GTK_WIDGET_CLASS(kclass)->enter_notify_event = bar_enter_notify_event; GTK_WIDGET_CLASS(kclass)->leave_notify_event = bar_leave_notify_event; } GtkWidget *bar_from_name ( gchar *name ) { if(!bar_list) return NULL; return g_hash_table_lookup(bar_list,name?name:"sfwbar"); } GtkWidget *bar_grid_from_name ( gchar *addr ) { GtkWidget *bar; BarPrivate *priv; GtkWidget *widget = NULL; gchar *grid, *ptr, *name; if(!addr) addr = "sfwbar"; if(!g_ascii_strcasecmp(addr,"*")) return NULL; ptr = strchr(addr,':'); if(ptr) { grid = ptr + 1; if(ptr == addr) name = g_strdup("sfwbar"); else name = g_strndup(addr,ptr-addr); } else { grid = NULL; name = g_strdup(addr); } if(!g_ascii_strcasecmp(name,"*")) { g_message( "invalid bar name '*' in grid address %s, defaulting to 'sfwbar'", addr); g_free(name); name = g_strdup("sfwbar"); } bar = bar_from_name(name); if(!bar) bar = bar_new(name); g_free(name); priv = bar_get_instance_private(BAR(bar)); if(grid && !g_ascii_strcasecmp(grid,"center")) widget = priv->center; else if(grid && !g_ascii_strcasecmp(grid,"end")) widget = priv->end; else widget = priv->start; if(widget) return widget; widget = grid_new(); gtk_widget_set_name(base_widget_get_child(widget),"layout"); if(grid && !g_ascii_strcasecmp(grid,"center")) { gtk_box_set_center_widget(GTK_BOX(priv->box),widget); priv->center = widget; } else if(grid && !g_ascii_strcasecmp(grid,"end")) { gtk_box_pack_end(GTK_BOX(priv->box),widget,TRUE,TRUE,0); priv->end = widget; } else { gtk_box_pack_start(GTK_BOX(priv->box),widget,TRUE,TRUE,0); priv->start = widget; } return widget; } gboolean bar_address_all ( GtkWidget *self, gchar *value, void (*bar_func)( GtkWidget *, gchar * ) ) { void *bar,*key; GHashTableIter iter; if(!self) { if(bar_list) { g_hash_table_iter_init(&iter,bar_list); while(g_hash_table_iter_next(&iter,&key,&bar)) bar_set_mirrors(bar, value); } return TRUE; } return FALSE; } void bar_set_mirrors ( GtkWidget *self, gchar *mirror ) { BarPrivate *priv; if(bar_address_all(self, mirror, bar_set_mirrors)) return; g_return_if_fail(IS_BAR(self)); priv = bar_get_instance_private(BAR(self)); g_strfreev(priv->mirror_targets); priv->mirror_targets = g_strsplit(mirror,";",-1); bar_update_monitor(self); } void bar_set_mirror_blocks ( GtkWidget *self, gchar *mirror ) { BarPrivate *priv; if(bar_address_all(self, mirror, bar_set_mirror_blocks)) return; g_return_if_fail(IS_BAR(self)); priv = bar_get_instance_private(BAR(self)); g_strfreev(priv->mirror_blocks); priv->mirror_blocks = g_strsplit(mirror,";",-1); bar_update_monitor(self); } void bar_set_id ( GtkWidget *self, gchar *id ) { BarPrivate *priv; if(bar_address_all(self, id, bar_set_id)) return; g_return_if_fail(IS_BAR(self)); priv = bar_get_instance_private(BAR(self)); g_free(priv->bar_id); priv->bar_id = g_strdup(id); g_list_foreach(priv->mirror_children,(GFunc)bar_set_id,id); } void bar_visibility_toggle_all ( gpointer d ) { bar_set_visibility ( NULL, NULL, 't' ); } void bar_set_visibility ( GtkWidget *self, const gchar *id, gchar state ) { BarPrivate *priv; gboolean visible; void *bar,*key; GHashTableIter iter; GList *liter; if(!self) { if(bar_list) { g_hash_table_iter_init(&iter,bar_list); while(g_hash_table_iter_next(&iter,&key,&bar)) bar_set_visibility(bar, id, state); } return; } g_return_if_fail(IS_BAR(self)); priv = bar_get_instance_private(BAR(self)); if(id && priv->bar_id && g_strcmp0(priv->bar_id,id)) return; if(state == 't') priv->visible = !priv->visible; else if(state == 'h') priv->visible = FALSE; else if(state != 'x' && state != 'v') priv->visible = TRUE; visible = priv->visible || (state == 'v'); if(!visible && gtk_widget_is_visible(self)) gtk_widget_hide(self); else if(!gtk_widget_is_visible(self)) { bar_update_monitor(self); gtk_widget_show_now(self); } for(liter=priv->mirror_children;liter;liter=g_list_next(liter)) bar_set_visibility(liter->data,id,state); } gint bar_get_toplevel_dir ( GtkWidget *widget ) { GtkWidget *toplevel; gint toplevel_dir; if(!widget) return GTK_POS_RIGHT; toplevel = gtk_widget_get_ancestor(widget,GTK_TYPE_WINDOW); if(!toplevel) return GTK_POS_RIGHT; gtk_widget_style_get(toplevel, "direction",&toplevel_dir,NULL); return toplevel_dir; } void bar_set_layer ( GtkWidget *self, gchar *layer_str ) { BarPrivate *priv; GtkLayerShellLayer layer; if(bar_address_all(self, layer_str, bar_set_layer)) return; g_return_if_fail(IS_BAR(self)); g_return_if_fail(layer_str!=NULL); priv = bar_get_instance_private(BAR(self)); g_free(priv->layer); priv->layer = g_strdup(layer_str); if(!g_ascii_strcasecmp(layer_str,"background")) layer = GTK_LAYER_SHELL_LAYER_BACKGROUND; else if(!g_ascii_strcasecmp(layer_str,"bottom")) layer = GTK_LAYER_SHELL_LAYER_BOTTOM; else if(!g_ascii_strcasecmp(layer_str,"overlay")) layer = GTK_LAYER_SHELL_LAYER_OVERLAY; else layer = GTK_LAYER_SHELL_LAYER_TOP; #if GTK_LAYER_VER_MINOR > 5 || GTK_LAYER_VER_MAJOR > 0 if(layer == gtk_layer_get_layer(GTK_WINDOW(self))) return; #endif gtk_layer_set_layer(GTK_WINDOW(self), layer); if(gtk_widget_is_visible(self)) { gtk_widget_hide(self); gtk_widget_show_now(self); } g_list_foreach(priv->mirror_children,(GFunc)bar_set_layer,layer_str); } void bar_set_exclusive_zone ( GtkWidget *self, gchar *zone ) { BarPrivate *priv; if(bar_address_all(self, zone, bar_set_exclusive_zone)) return; g_return_if_fail(IS_BAR(self)); g_return_if_fail(zone!=NULL); priv = bar_get_instance_private(BAR(self)); g_free(priv->ezone); priv->ezone = g_strdup(zone); if(!g_ascii_strcasecmp(zone,"auto")) gtk_layer_auto_exclusive_zone_enable(GTK_WINDOW(self)); else gtk_layer_set_exclusive_zone(GTK_WINDOW(self), MAX(-1,g_ascii_strtoll(zone,NULL,10))); g_list_foreach(priv->mirror_children, (GFunc)bar_set_exclusive_zone, zone); } GdkMonitor *bar_get_monitor ( GtkWidget *self ) { BarPrivate *priv; g_return_val_if_fail(IS_BAR(self),NULL); priv = bar_get_instance_private(BAR(self)); return priv->current_monitor; } gchar *bar_get_output ( GtkWidget *self ) { BarPrivate *priv; priv = bar_get_instance_private( BAR(gtk_widget_get_ancestor(self, GTK_TYPE_WINDOW))); if(!priv->current_monitor) return NULL; else return g_object_get_data( G_OBJECT(priv->current_monitor), "xdg_name"); } gboolean bar_update_monitor ( GtkWidget *self ) { BarPrivate *priv; GdkDisplay *gdisp; GdkMonitor *gmon,*match; GList *iter; gint nmon, i; gchar *output; gboolean present; g_return_val_if_fail(IS_BAR(self),FALSE); priv = bar_get_instance_private(BAR(self)); if(!xdg_output_check() ) return TRUE; gdisp = gdk_display_get_default(); if(priv->jump) { match = gdk_display_get_primary_monitor(gdisp); if(!match) match = gdk_display_get_monitor(gdisp,0); } else match = NULL; nmon = gdk_display_get_n_monitors(gdisp); if(priv->output) for(i=0; ioutput)) match = gmon; } gtk_widget_hide(self); priv->current_monitor = match; if(match) { gtk_layer_set_monitor(GTK_WINDOW(self), match); if(priv->visible) { gtk_widget_show_now(self); taskbar_invalidate_conditional(); } } /* remove any mirrors from new primary output */ for(iter=priv->mirror_children; iter; iter=g_list_next(iter)) if(bar_get_monitor(iter->data) == priv->current_monitor) bar_destroy(iter->data); /* add mirrors to any outputs where they are missing */ for(i=0; imirror_children; iter; iter=g_list_next(iter)) if(bar_get_monitor(iter->data) == gmon) present = TRUE; if(!present && gmon != priv->current_monitor && pattern_match(priv->mirror_targets,output) && !pattern_match(priv->mirror_blocks,output) ) bar_mirror(self, gmon); } return FALSE; } void bar_set_monitor ( GtkWidget *self, gchar *monitor ) { BarPrivate *priv; gchar *mon_name; if(bar_address_all(self, monitor, bar_set_monitor)) return; g_return_if_fail(IS_BAR(self)); g_return_if_fail(monitor!=NULL); priv = bar_get_instance_private(BAR(self)); if(!g_ascii_strncasecmp(monitor,"static:",7)) { priv->jump = FALSE; mon_name = monitor+7; } else { priv->jump = TRUE; mon_name = monitor; } if(!priv->output || g_ascii_strcasecmp(priv->output, mon_name)) { g_free(priv->output); priv->output = g_strdup(mon_name); bar_update_monitor(self); } } void bar_monitor_added_cb ( GdkDisplay *gdisp, GdkMonitor *gmon ) { GHashTableIter iter; void *key, *bar; char trigger[256]; xdg_output_new(gmon); g_hash_table_iter_init(&iter,bar_list); while(g_hash_table_iter_next(&iter,&key,&bar)) g_idle_add((GSourceFunc)bar_update_monitor,bar); g_snprintf(trigger,255,"%s_connected", (gchar *)g_object_get_data(G_OBJECT(gmon),"xdg_name")); g_idle_add((GSourceFunc)base_widget_emit_trigger,trigger); } void bar_monitor_removed_cb ( GdkDisplay *gdisp, GdkMonitor *gmon ) { BarPrivate *priv; GHashTableIter iter; void *key, *bar; GList *liter; static char trigger[256]; g_hash_table_iter_init(&iter,bar_list); while(g_hash_table_iter_next(&iter,&key,&bar)) { priv = bar_get_instance_private(BAR(bar)); for(liter=priv->mirror_children; liter; liter=g_list_next(liter)) if(bar_get_monitor(liter->data) == gmon) { bar_destroy(liter->data); break; } bar_update_monitor(bar); } g_snprintf(trigger,255,"%s_disconnected", (gchar *)g_object_get_data(G_OBJECT(gmon),"xdg_name")); g_idle_add((GSourceFunc)base_widget_emit_trigger,trigger); } void bar_set_size ( GtkWidget *self, gchar *size ) { BarPrivate *priv; gdouble number; gchar *end; GdkRectangle rect; GdkWindow *win; gint toplevel_dir; if(bar_address_all(self, size, bar_set_size)) return; g_return_if_fail(IS_BAR(self)); g_return_if_fail(size!=NULL); priv = bar_get_instance_private(BAR(self)); g_free(priv->size); priv->size = g_strdup(size); number = g_ascii_strtod(size, &end); win = gtk_widget_get_window(self); gdk_monitor_get_geometry( gdk_display_get_monitor_at_window( gdk_window_get_display(win),win), &rect ); toplevel_dir = bar_get_toplevel_dir(self); if ( toplevel_dir == GTK_POS_BOTTOM || toplevel_dir == GTK_POS_TOP ) { if(*end == '%') number = number * rect.width / 100; if ( number >= rect.width ) { gtk_layer_set_anchor(GTK_WINDOW(self), GTK_LAYER_SHELL_EDGE_LEFT,TRUE); gtk_layer_set_anchor(GTK_WINDOW(self), GTK_LAYER_SHELL_EDGE_RIGHT,TRUE); } else { gtk_layer_set_anchor(GTK_WINDOW(self), GTK_LAYER_SHELL_EDGE_LEFT, FALSE ); gtk_layer_set_anchor(GTK_WINDOW(self), GTK_LAYER_SHELL_EDGE_RIGHT, FALSE ); gtk_widget_set_size_request(self,(gint)number,-1); } } else { if(*end == '%') number = number * rect.height / 100; if ( number >= rect.height ) { gtk_layer_set_anchor (GTK_WINDOW(self), GTK_LAYER_SHELL_EDGE_TOP, TRUE ); gtk_layer_set_anchor (GTK_WINDOW(self), GTK_LAYER_SHELL_EDGE_BOTTOM, TRUE ); } else { gtk_layer_set_anchor (GTK_WINDOW(self), GTK_LAYER_SHELL_EDGE_TOP, FALSE ); gtk_layer_set_anchor (GTK_WINDOW(self), GTK_LAYER_SHELL_EDGE_BOTTOM, FALSE ); gtk_widget_set_size_request(self,-1,(gint)number); } } g_list_foreach(priv->mirror_children,(GFunc)bar_set_size,size); } void bar_sensor_cancel_hide( GtkWidget *self ) { BarPrivate *priv; g_return_if_fail(IS_BAR(self)); priv = bar_get_instance_private(BAR(self)); if(priv->sensor_handle) { g_source_remove(priv->sensor_handle); priv->sensor_handle = 0; } } void bar_set_sensor ( GtkWidget *self, gchar *delay_str ) { BarPrivate *priv; if(bar_address_all(self, delay_str, bar_set_sensor)) return; g_return_if_fail(IS_BAR(self)); priv = bar_get_instance_private(BAR(self)); priv->sensor_timeout = g_ascii_strtoll(delay_str,NULL,10); if(priv->sensor_timeout) { if(!priv->sensor) { priv->sensor = gtk_grid_new(); g_object_ref_sink(priv->sensor); css_add_class(priv->sensor,"sensor"); gtk_widget_add_events(priv->sensor, GDK_STRUCTURE_MASK); gtk_widget_add_events(priv->box, GDK_STRUCTURE_MASK); gtk_widget_show(priv->sensor); } bar_sensor_hide(self); priv->sensor_block = FALSE; } else if(priv->sensor_handle) bar_sensor_show_bar(self); g_list_foreach(priv->mirror_children,(GFunc)bar_set_sensor,delay_str); } void bar_handle_direction ( GtkWidget *self ) { BarPrivate *priv; gint toplevel_dir; if(!IS_BAR(self)) return; priv = bar_get_instance_private(BAR(self)); gtk_widget_style_get(self,"direction",&toplevel_dir,NULL); if(priv->dir == toplevel_dir) return; priv->dir = toplevel_dir; gtk_layer_set_anchor(GTK_WINDOW(self),GTK_LAYER_SHELL_EDGE_LEFT, !(toplevel_dir==GTK_POS_RIGHT)); gtk_layer_set_anchor(GTK_WINDOW(self),GTK_LAYER_SHELL_EDGE_RIGHT, !(toplevel_dir==GTK_POS_LEFT)); gtk_layer_set_anchor(GTK_WINDOW(self),GTK_LAYER_SHELL_EDGE_BOTTOM, !(toplevel_dir==GTK_POS_TOP)); gtk_layer_set_anchor(GTK_WINDOW(self),GTK_LAYER_SHELL_EDGE_TOP, !(toplevel_dir==GTK_POS_BOTTOM)); if(priv->start) g_object_ref(priv->start); if(priv->center) g_object_ref(priv->center); if(priv->end) g_object_ref(priv->end); if(priv->box && gtk_bin_get_child(GTK_BIN(self))==priv->box) gtk_container_remove(GTK_CONTAINER(self),priv->box); if(priv->box) g_object_unref(priv->box); if(toplevel_dir == GTK_POS_LEFT || toplevel_dir == GTK_POS_RIGHT) priv->box = gtk_box_new(GTK_ORIENTATION_VERTICAL,0); else priv->box = gtk_box_new(GTK_ORIENTATION_HORIZONTAL,0); g_object_ref_sink(priv->box); if(!gtk_bin_get_child(GTK_BIN(self))) gtk_container_add(GTK_CONTAINER(self), priv->box); if(priv->start) gtk_box_pack_start(GTK_BOX(priv->box),priv->start,TRUE,TRUE,0); if(priv->center) gtk_box_set_center_widget(GTK_BOX(priv->box),priv->center); if(priv->end) gtk_box_pack_end(GTK_BOX(priv->box),priv->end,TRUE,TRUE,0); } GtkWidget *bar_new ( gchar *name ) { GtkWidget *self; BarPrivate *priv; self = GTK_WIDGET(g_object_new(bar_get_type(), NULL)); priv = bar_get_instance_private(BAR(self)); priv->name = g_strdup(name); priv->visible = TRUE; priv->current_monitor = wayland_monitor_get_default(); priv->output = g_strdup( g_object_get_data(G_OBJECT(priv->current_monitor),"xdg_name")); priv->dir = -1; gtk_layer_init_for_window (GTK_WINDOW(self)); gtk_widget_set_name(self,name); gtk_layer_auto_exclusive_zone_enable (GTK_WINDOW(self)); gtk_layer_set_keyboard_interactivity(GTK_WINDOW(self),FALSE); gtk_layer_set_layer(GTK_WINDOW(self),GTK_LAYER_SHELL_LAYER_TOP); gtk_layer_set_monitor(GTK_WINDOW(self),priv->current_monitor); bar_handle_direction(self); if(priv->name) { if(!bar_list) bar_list = g_hash_table_new((GHashFunc)str_nhash,(GEqualFunc)str_nequal); g_hash_table_insert(bar_list, priv->name, self); } return self; } GtkWidget *bar_mirror ( GtkWidget *src, GdkMonitor *monitor ) { GtkWidget *self; BarPrivate *spriv,*dpriv; gchar *tmp; g_return_val_if_fail(IS_BAR(src),NULL); self = bar_new(NULL); spriv = bar_get_instance_private(BAR(src)); dpriv = bar_get_instance_private(BAR(self)); gtk_widget_set_name(self,gtk_widget_get_name(src)); bar_handle_direction(self); if(spriv->start) { dpriv->start = base_widget_mirror(spriv->start); gtk_box_pack_start(GTK_BOX(dpriv->box),dpriv->start,TRUE,TRUE,0); } if(spriv->center) { dpriv->center = base_widget_mirror(spriv->center); gtk_box_set_center_widget(GTK_BOX(dpriv->box),dpriv->center); } if(spriv->end) { dpriv->end = base_widget_mirror(spriv->end); gtk_box_pack_end(GTK_BOX(dpriv->box),dpriv->end,TRUE,TRUE,0); } dpriv->visible = spriv->visible; dpriv->hidden = spriv->hidden; dpriv->jump = spriv->jump; dpriv->bar_id = g_strdup(spriv->bar_id); spriv->mirror_children = g_list_prepend(spriv->mirror_children, self); dpriv->mirror_parent = src; dpriv->current_monitor = monitor; dpriv->output = g_strdup(g_object_get_data(G_OBJECT(monitor),"xdg_name")); mirrors = g_list_prepend(mirrors,self); tmp = g_strdup_printf("%ld",spriv->sensor_timeout); bar_set_sensor(self, tmp); g_free(tmp); // gtk_application_add_window(app,GTK_WINDOW(self)); gtk_layer_set_monitor(GTK_WINDOW(self),monitor); gtk_widget_show(self); css_widget_cascade(GTK_WIDGET(self),NULL); if(spriv->size) bar_set_size(self,spriv->size); if(spriv->layer) bar_set_layer(self,spriv->layer); if(spriv->ezone) bar_set_exclusive_zone(self,spriv->ezone); return self; } void bar_set_theme ( gchar *new_theme ) { GtkSettings *setts; setts = gtk_settings_get_default(); g_object_set(G_OBJECT(setts),"gtk-application-prefer-dark-theme",FALSE,NULL); g_object_set(G_OBJECT(setts),"gtk-theme-name",new_theme,NULL); g_free(new_theme); } sfwbar-1.0~beta13/src/bar.h000066400000000000000000000044551450657570000155610ustar00rootroot00000000000000#ifndef __BAR_H__ #define __BAR_H__ #include #define BAR_TYPE (bar_get_type()) #define BAR(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), BAR_TYPE, Bar)) #define BAR_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), BAR_TYPE, BarClass)) #define IS_BAR(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), BAR_TYPE)) #define IS_BAR_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), BAR_TYPE)) #define BAR_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), BAR_TYPE, BarClass)) typedef struct _Bar Bar; typedef struct _BarClass BarClass; struct _Bar { GtkWindow parent_class; }; struct _BarClass { GtkWindowClass parent_class; void (*old_hide)( GtkWidget * ); }; typedef struct _BarPrivate BarPrivate; struct _BarPrivate { gchar *name; gchar *size; gchar *ezone; gchar *layer; gchar *bar_id; gint dir; GtkWidget *start, *center, *end; GtkWidget *box, *sensor; gint64 sensor_timeout; gboolean sensor_state, sensor_block; guint sensor_handle; GList *sensor_refs; gboolean hidden; gboolean jump; gboolean visible; gchar *output; GdkMonitor *current_monitor; gchar **mirror_targets; gchar **mirror_blocks; GList *mirror_children; GtkWidget *mirror_parent; }; GtkWidget *bar_new ( gchar * ); void bar_set_monitor ( GtkWidget *, gchar * ); void bar_set_layer ( GtkWidget *, gchar * ); void bar_set_size ( GtkWidget *, gchar * ); void bar_set_exclusive_zone ( GtkWidget *, gchar * ); gchar *bar_get_output ( GtkWidget * ); gint bar_get_toplevel_dir ( GtkWidget * ); void bar_set_id ( GtkWidget *, gchar * ); void bar_set_sensor ( GtkWidget *self, gchar *timeout ); void bar_set_mirrors ( GtkWidget *self, gchar *mirror ); void bar_set_mirror_blocks ( GtkWidget *self, gchar *mirror ); void bar_set_visibility ( GtkWidget *, const gchar *, gchar ); void bar_visibility_toggle_all ( gpointer d ); void bar_monitor_added_cb ( GdkDisplay *, GdkMonitor * ); void bar_monitor_removed_cb ( GdkDisplay *, GdkMonitor * ); gboolean bar_update_monitor ( GtkWidget * ); void bar_save_monitor ( GtkWidget * ); GtkWidget *bar_from_name ( gchar *name ); GtkWidget *bar_grid_from_name ( gchar *addr ); void bar_set_theme ( gchar *new_theme ); GtkWidget *bar_mirror ( GtkWidget *, GdkMonitor * ); void bar_handle_direction ( GtkWidget *self ); void bar_sensor_cancel_hide ( GtkWidget *self ); #endif sfwbar-1.0~beta13/src/basewidget.c000066400000000000000000000516331450657570000171260ustar00rootroot00000000000000/* This entire file is licensed under GNU General Public License v3.0 * * Copyright 2022- sfwbar maintainers */ #include #include "expr.h" #include "basewidget.h" #include "flowgrid.h" #include "action.h" #include "module.h" G_DEFINE_TYPE_WITH_CODE (BaseWidget, base_widget, GTK_TYPE_EVENT_BOX, G_ADD_PRIVATE (BaseWidget)); static GHashTable *base_widget_id_map; static GList *widgets_scan; static GMutex widget_mutex; static gint64 base_widget_default_id = 0; static void base_widget_attachment_free ( base_widget_attachment_t *attach ) { if(!attach) return; action_free(attach->action, NULL); g_free(attach); } static base_widget_attachment_t *base_widget_attachment_dup ( base_widget_attachment_t *src ) { base_widget_attachment_t *dest; if(!src) return NULL; dest = g_malloc0(sizeof(base_widget_attachment_t)); dest->event = src->event; dest->mods = src->mods; dest->action = action_dup(src->action); return dest; } static void base_widget_destroy ( GtkWidget *self ) { BaseWidgetPrivate *priv,*ppriv; g_return_if_fail(IS_BASE_WIDGET(self)); priv = base_widget_get_instance_private(BASE_WIDGET(self)); g_mutex_lock(&widget_mutex); widgets_scan = g_list_remove(widgets_scan, self); g_mutex_unlock(&widget_mutex); if(priv->mirror_parent) { ppriv = base_widget_get_instance_private(BASE_WIDGET(priv->mirror_parent)); ppriv->mirror_children = g_list_remove(ppriv->mirror_children, self); priv->mirror_parent = NULL; } if(base_widget_id_map && priv->id) g_hash_table_remove(base_widget_id_map, priv->id); g_list_free_full(priv->css, g_free); priv->css = NULL; g_clear_pointer(&priv->id, g_free); g_clear_pointer(&priv->value, expr_cache_free); g_clear_pointer(&priv->style, expr_cache_free); g_clear_pointer(&priv->tooltip, expr_cache_free); g_clear_pointer(&priv->trigger, g_free); g_list_free_full(g_steal_pointer(&priv->actions), (GDestroyNotify)base_widget_attachment_free); GTK_WIDGET_CLASS(base_widget_parent_class)->destroy(self); } static void base_widget_size_allocate ( GtkWidget *self, GtkAllocation *alloc ) { BaseWidgetPrivate *priv; gint m, n; g_return_if_fail(IS_BASE_WIDGET(self)); priv = base_widget_get_instance_private(BASE_WIDGET(self)); if(priv->maxw) { gtk_widget_get_preferred_width(gtk_bin_get_child(GTK_BIN(self)),&m,&n); alloc->width = MAX(m,MIN(priv->maxw,alloc->width)); } if(priv->maxh) { gtk_widget_get_preferred_height(gtk_bin_get_child(GTK_BIN(self)),&m,&n); alloc->height = MAX(m,MIN(priv->maxh,alloc->height)); } BASE_WIDGET_GET_CLASS(self)->old_size_allocate(self,alloc); } static void base_widget_get_pref_width ( GtkWidget *self, gint *m, gint *n ) { BaseWidgetPrivate *priv; GtkWidget *child; *m = 0; *n = 0; g_return_if_fail(IS_BASE_WIDGET(self)); child = gtk_bin_get_child(GTK_BIN(self)); if(child && gtk_widget_get_visible(child)) { priv = base_widget_get_instance_private(BASE_WIDGET(self)); gtk_widget_get_preferred_width(child,m,n); if(priv->maxw) *n = MAX(*m,MIN(*n,priv->maxw)); } } static void base_widget_get_pref_height ( GtkWidget *self, gint *m, gint *n ) { BaseWidgetPrivate *priv; GtkWidget *child; *m = 0; *n = 0; g_return_if_fail(IS_BASE_WIDGET(self)); child = gtk_bin_get_child(GTK_BIN(self)); if(child && gtk_widget_get_visible(child)) { priv = base_widget_get_instance_private(BASE_WIDGET(self)); gtk_widget_get_preferred_height(child,m,n); if(priv->maxh) *n = MAX(*m,MIN(*n,priv->maxh)); } } GdkModifierType base_widget_get_modifiers ( GtkWidget *self ) { GdkModifierType state; GtkWindow *win; win = GTK_WINDOW(gtk_widget_get_ancestor(self, GTK_TYPE_WINDOW)); if(gtk_window_get_window_type(win)==GTK_WINDOW_POPUP) win = g_object_get_data(G_OBJECT(win),"parent_window"); if(win && gtk_layer_is_layer_window(win)) { gtk_layer_set_keyboard_mode(win, GTK_LAYER_SHELL_KEYBOARD_MODE_EXCLUSIVE); gtk_main_iteration(); gtk_main_iteration(); gtk_main_iteration(); state = gdk_keymap_get_modifier_state(gdk_keymap_get_for_display( gdk_display_get_default())) & gtk_accelerator_get_default_mod_mask(); gtk_layer_set_keyboard_mode(win, GTK_LAYER_SHELL_KEYBOARD_MODE_NONE); } else state = 0; g_debug("modifier state: %x", state); return state; } gboolean base_widget_check_action_slot ( GtkWidget *self, gint slot ) { BaseWidgetPrivate *priv; GList *iter; priv = base_widget_get_instance_private(BASE_WIDGET(self)); for(iter=priv->actions; iter; iter=g_list_next(iter)) if(((base_widget_attachment_t *)(iter->data))->event == slot) break; return !!iter; } static gboolean base_widget_button_release_event ( GtkWidget *self, GdkEventButton *ev ) { g_return_val_if_fail(IS_BASE_WIDGET(self),FALSE); if (ev->type != GDK_BUTTON_RELEASE || ev->button < 1 || ev->button > 3 ) return FALSE; return base_widget_action_exec(self, ev->button, (GdkEvent *)ev); } static gboolean base_widget_scroll_event ( GtkWidget *self, GdkEventScroll *ev ) { gint slot; g_return_val_if_fail(IS_BASE_WIDGET(self),FALSE); switch(ev->direction) { case GDK_SCROLL_UP: slot = 4; break; case GDK_SCROLL_DOWN: slot = 5; break; case GDK_SCROLL_LEFT: slot = 6; break; case GDK_SCROLL_RIGHT: slot = 7; break; default: return FALSE; } return base_widget_action_exec(self, slot, (GdkEvent *)ev); } static gboolean base_widget_action_exec_impl ( GtkWidget *self, gint slot, GdkEvent *ev ) { if(!base_widget_check_action_slot(self, slot)) return FALSE; action_exec(self, base_widget_get_action(self, slot, base_widget_get_modifiers(self)), (GdkEvent *)ev, wintree_from_id(wintree_get_focus()), NULL); return TRUE; } static void base_widget_class_init ( BaseWidgetClass *kclass ) { GTK_WIDGET_CLASS(kclass)->destroy = base_widget_destroy; kclass->old_size_allocate = GTK_WIDGET_CLASS(kclass)->size_allocate; kclass->action_exec = base_widget_action_exec_impl; GTK_WIDGET_CLASS(kclass)->size_allocate = base_widget_size_allocate; GTK_WIDGET_CLASS(kclass)->get_preferred_width = base_widget_get_pref_width; GTK_WIDGET_CLASS(kclass)->get_preferred_height = base_widget_get_pref_height; GTK_WIDGET_CLASS(kclass)->button_release_event = base_widget_button_release_event; GTK_WIDGET_CLASS(kclass)->scroll_event = base_widget_scroll_event; } static void base_widget_init ( BaseWidget *self ) { BaseWidgetPrivate *priv; g_return_if_fail(IS_BASE_WIDGET(self)); priv = base_widget_get_instance_private(BASE_WIDGET(self)); priv->value = expr_cache_new(); priv->style = expr_cache_new(); priv->tooltip = expr_cache_new(); priv->interval = 1000000; priv->dir = GTK_POS_RIGHT; priv->rect.x = -1; priv->rect.y = -1; priv->rect.width = 1; priv->rect.height = 1; base_widget_set_id(GTK_WIDGET(self), NULL); } static gboolean base_widget_tooltip_update ( GtkWidget *self, gint x, gint y, gboolean kbmode, GtkTooltip *tooltip, gpointer data ) { BaseWidgetPrivate *priv; g_return_val_if_fail(IS_BASE_WIDGET(self),FALSE); priv = base_widget_get_instance_private(BASE_WIDGET(self)); if(!priv->tooltip) return FALSE; expr_cache_eval(priv->tooltip); if(!priv->tooltip->cache) return FALSE; gtk_tooltip_set_markup(tooltip, priv->tooltip->cache); return TRUE; } GtkWidget *base_widget_get_child ( GtkWidget *self ) { g_return_val_if_fail(IS_BASE_WIDGET(self),NULL); if(BASE_WIDGET_GET_CLASS(self)->get_child) return BASE_WIDGET_GET_CLASS(self)->get_child(self); else return NULL; } gboolean base_widget_update_value ( GtkWidget *self ) { BaseWidgetPrivate *priv; GList *iter; g_return_val_if_fail(IS_BASE_WIDGET(self),FALSE); priv = base_widget_get_instance_private(BASE_WIDGET(self)); if(BASE_WIDGET_GET_CLASS(self)->update_value) BASE_WIDGET_GET_CLASS(self)->update_value(self); for(iter=priv->mirror_children;iter;iter=g_list_next(iter)) BASE_WIDGET_GET_CLASS(self)->update_value(iter->data); return FALSE; } gboolean base_widget_style ( GtkWidget *self ) { BaseWidgetPrivate *priv; GtkWidget *child; GList *iter; g_return_val_if_fail(IS_BASE_WIDGET(self),FALSE); if(!BASE_WIDGET_GET_CLASS(self)->get_child) return FALSE; priv = base_widget_get_instance_private(BASE_WIDGET(self)); child = BASE_WIDGET_GET_CLASS(self)->get_child(self); if(priv->mirror_parent) priv = base_widget_get_instance_private(BASE_WIDGET(priv->mirror_parent)); gtk_widget_set_name(child,priv->style->cache); css_widget_cascade(child,NULL); priv = base_widget_get_instance_private(BASE_WIDGET(self)); for(iter=priv->mirror_children;iter;iter=g_list_next(iter)) base_widget_style(iter->data); return FALSE; } void base_widget_set_tooltip ( GtkWidget *self, gchar *tooltip ) { BaseWidgetPrivate *priv; g_return_if_fail(IS_BASE_WIDGET(self)); priv = base_widget_get_instance_private(BASE_WIDGET(self)); if(!priv->tooltip) return; g_free(priv->tooltip->definition); priv->tooltip->definition = tooltip; priv->tooltip->eval = TRUE; priv->value->widget = self; if(!tooltip) { gtk_widget_set_has_tooltip(self,FALSE); return; } if(expr_cache_eval(priv->tooltip)) { gtk_widget_set_has_tooltip(self,TRUE); gtk_widget_set_tooltip_markup(self,priv->tooltip->cache); } if(!priv->tooltip_h) priv->tooltip_h = g_signal_connect(self,"query-tooltip", G_CALLBACK(base_widget_tooltip_update),self); } void base_widget_set_value ( GtkWidget *self, gchar *value ) { BaseWidgetPrivate *priv; g_return_if_fail(IS_BASE_WIDGET(self)); priv = base_widget_get_instance_private(BASE_WIDGET(self)); g_free(priv->value->definition); priv->value->definition = value; priv->value->eval = TRUE; priv->value->widget = self; if(expr_cache_eval(priv->value) || priv->always_update) base_widget_update_value(self); g_mutex_lock(&widget_mutex); if(!g_list_find(widgets_scan,self)) widgets_scan = g_list_append(widgets_scan,self); g_mutex_unlock(&widget_mutex); } void base_widget_set_style ( GtkWidget *self, gchar *style ) { BaseWidgetPrivate *priv; g_return_if_fail(IS_BASE_WIDGET(self)); priv = base_widget_get_instance_private(BASE_WIDGET(self)); g_free(priv->style->definition); priv->style->definition = style; priv->style->eval = TRUE; priv->value->widget = self; if(expr_cache_eval(priv->style)) { base_widget_style(self); } g_mutex_lock(&widget_mutex); if(!g_list_find(widgets_scan,self)) widgets_scan = g_list_append(widgets_scan,self); g_mutex_unlock(&widget_mutex); } void base_widget_set_trigger ( GtkWidget *self, gchar *trigger ) { BaseWidgetPrivate *priv; g_return_if_fail(IS_BASE_WIDGET(self)); priv = base_widget_get_instance_private(BASE_WIDGET(self)); g_free(priv->trigger); priv->trigger = trigger; } void base_widget_set_id ( GtkWidget *self, gchar *id ) { BaseWidgetPrivate *priv; g_return_if_fail(IS_BASE_WIDGET(self)); priv = base_widget_get_instance_private(BASE_WIDGET(self)); if(!base_widget_id_map) base_widget_id_map = g_hash_table_new_full((GHashFunc)str_nhash, (GEqualFunc)str_nequal, g_free,NULL); if(priv->id) g_hash_table_remove(base_widget_id_map, priv->id); g_free(priv->id); priv->id = id? id: g_strdup_printf("_w%ld", base_widget_default_id++); if(!g_hash_table_lookup(base_widget_id_map, priv->id)) g_hash_table_insert(base_widget_id_map, g_strdup(priv->id), self); } GtkWidget *base_widget_from_id ( gchar *id ) { if(!base_widget_id_map || !id) return NULL; return g_hash_table_lookup(base_widget_id_map,id); } void base_widget_set_interval ( GtkWidget *self, gint64 interval ) { BaseWidgetPrivate *priv; g_return_if_fail(IS_BASE_WIDGET(self)); priv = base_widget_get_instance_private(BASE_WIDGET(self)); priv->interval = interval; } void base_widget_set_state ( GtkWidget *self, guint16 mask, gboolean state ) { BaseWidgetPrivate *priv; g_return_if_fail(IS_BASE_WIDGET(self)); priv = base_widget_get_instance_private(BASE_WIDGET(self)); if(state) priv->user_state |= mask; else priv->user_state &= ~mask; } void base_widget_set_rect ( GtkWidget *self, GdkRectangle rect ) { BaseWidgetPrivate *priv; g_return_if_fail(IS_BASE_WIDGET(self)); priv = base_widget_get_instance_private(BASE_WIDGET(self)); priv->rect = rect; } void base_widget_attach ( GtkWidget *parent, GtkWidget *self, GtkWidget *sibling ) { BaseWidgetPrivate *priv; gint dir; g_return_if_fail(IS_BASE_WIDGET(self)); priv = base_widget_get_instance_private(BASE_WIDGET(self)); if(parent) { gtk_widget_style_get(parent,"direction",&dir,NULL); if( (priv->rect.x < 0) || (priv->rect.y < 0 ) ) gtk_grid_attach_next_to(GTK_GRID(parent),self,sibling,dir,1,1); else gtk_grid_attach(GTK_GRID(parent),self, priv->rect.x,priv->rect.y,priv->rect.width,priv->rect.height); } } void base_widget_set_max_width ( GtkWidget *self, guint x ) { BaseWidgetPrivate *priv; g_return_if_fail(IS_BASE_WIDGET(self)); priv = base_widget_get_instance_private(BASE_WIDGET(self)); priv->maxw = x; } void base_widget_set_max_height ( GtkWidget *self, guint x ) { BaseWidgetPrivate *priv; g_return_if_fail(IS_BASE_WIDGET(self)); priv = base_widget_get_instance_private(BASE_WIDGET(self)); priv->maxh = x; } gchar *base_widget_get_id ( GtkWidget *self ) { BaseWidgetPrivate *priv; g_return_val_if_fail(IS_BASE_WIDGET(self),NULL); priv = base_widget_get_instance_private(BASE_WIDGET(self)); return priv->id; } guint16 base_widget_get_state ( GtkWidget *self ) { BaseWidgetPrivate *priv; g_return_val_if_fail(IS_BASE_WIDGET(self),FALSE); priv = base_widget_get_instance_private(BASE_WIDGET(self)); return priv->user_state; } void base_widget_set_always_update ( GtkWidget *self, gboolean update ) { BaseWidgetPrivate *priv; g_return_if_fail(IS_BASE_WIDGET(self)); priv = base_widget_get_instance_private(BASE_WIDGET(self)); priv->always_update = update; } void base_widget_set_next_poll ( GtkWidget *self, gint64 ctime ) { BaseWidgetPrivate *priv; g_return_if_fail(IS_BASE_WIDGET(self)); priv = base_widget_get_instance_private(BASE_WIDGET(self)); if(priv->trigger) return; while(priv->next_poll <= ctime) priv->next_poll += priv->interval; } gint64 base_widget_get_next_poll ( GtkWidget *self ) { BaseWidgetPrivate *priv; g_return_val_if_fail(IS_BASE_WIDGET(self),G_MAXINT64); priv = base_widget_get_instance_private(BASE_WIDGET(self)); if(priv->trigger || !priv->interval) return G_MAXINT64; if(!priv->value->eval && !priv->style->eval) return G_MAXINT64; return priv->next_poll; } action_t *base_widget_get_action ( GtkWidget *self, gint n, GdkModifierType mods ) { BaseWidgetPrivate *priv; base_widget_attachment_t *attach; GList *iter; g_return_val_if_fail(IS_BASE_WIDGET(self),NULL); priv = base_widget_get_instance_private(BASE_WIDGET(self)); for(iter=priv->actions; iter; iter=g_list_next(iter)) { attach = iter->data; if(attach->event == n && attach->mods == mods) return attach->action; } return NULL; } gboolean base_widget_action_exec ( GtkWidget *self, gint slot, GdkEvent *ev ) { g_return_val_if_fail(IS_BASE_WIDGET(self), FALSE); if(!BASE_WIDGET_GET_CLASS(self)->action_exec) return FALSE; return BASE_WIDGET_GET_CLASS(self)->action_exec(self, slot, ev); } gchar *base_widget_get_value ( GtkWidget *self ) { BaseWidgetPrivate *priv; g_return_val_if_fail(IS_BASE_WIDGET(self),NULL); priv = base_widget_get_instance_private(BASE_WIDGET(self)); if(priv->mirror_parent) priv = base_widget_get_instance_private(BASE_WIDGET(priv->mirror_parent)); return priv->value->cache; } void base_widget_set_css ( GtkWidget *self, gchar *css ) { BaseWidgetPrivate *priv; g_return_if_fail(IS_BASE_WIDGET(self)); priv = base_widget_get_instance_private(BASE_WIDGET(self)); if(!css || g_list_find_custom(priv->css, css, (GCompareFunc)g_strcmp0)) return; priv->css = g_list_append(priv->css,g_strdup(css)); css_widget_apply(base_widget_get_child(self),css); } void base_widget_set_action ( GtkWidget *self, gint n, GdkModifierType mods, action_t *action ) { BaseWidgetPrivate *priv; base_widget_attachment_t *attach; GList *iter; g_return_if_fail(IS_BASE_WIDGET(self)); priv = base_widget_get_instance_private(BASE_WIDGET(self)); if(n<0 || n>=8) return; for(iter=priv->actions; iter; iter=g_list_next(iter)) { attach = iter->data; if(attach->event == n && attach->mods == mods) break; } if(iter) { attach = iter->data; action_free(attach->action, NULL); } else { attach = g_malloc0(sizeof(base_widget_attachment_t)); attach->event = n; attach->mods = mods; priv->actions = g_list_prepend(priv->actions, attach); } attach->action = action; if(IS_FLOW_GRID(base_widget_get_child(self))) return; if(n>=1 && n<=3) gtk_widget_add_events(GTK_WIDGET(self), GDK_BUTTON_RELEASE_MASK); else if(n>=4 && n<=7) gtk_widget_add_events(GTK_WIDGET(self),GDK_SCROLL_MASK); } void base_widget_copy_actions ( GtkWidget *dest, GtkWidget *src ) { BaseWidgetPrivate *spriv, *dpriv; GList *iter; g_return_if_fail(IS_BASE_WIDGET(dest) && IS_BASE_WIDGET(src)); spriv = base_widget_get_instance_private(BASE_WIDGET(src)); dpriv = base_widget_get_instance_private(BASE_WIDGET(dest)); for(iter=spriv->actions; iter; iter=g_list_next(iter)) dpriv->actions = g_list_prepend(dpriv->actions, base_widget_attachment_dup(iter->data)); } void base_widget_copy_properties ( GtkWidget *dest, GtkWidget *src ) { BaseWidgetPrivate *spriv, *dpriv; GList *iter; g_return_if_fail(IS_BASE_WIDGET(dest) && IS_BASE_WIDGET(src)); spriv = base_widget_get_instance_private(BASE_WIDGET(src)); dpriv = base_widget_get_instance_private(BASE_WIDGET(dest)); base_widget_copy_actions(dest, src); if(spriv->tooltip) base_widget_set_tooltip( dest, spriv->tooltip->definition ); if(spriv->trigger) base_widget_set_trigger( dest, spriv->trigger ); else base_widget_set_interval( dest, spriv->interval ); base_widget_set_max_width( dest, spriv->maxw ); base_widget_set_max_height( dest, spriv->maxh ); base_widget_set_state( dest, spriv->user_state, TRUE ); base_widget_set_rect( dest, spriv->rect ); for(iter=spriv->css;iter;iter=g_list_next(iter)) css_widget_apply(base_widget_get_child(dest), g_strdup(iter->data)); if(!g_list_find(spriv->mirror_children, dest)) { spriv->mirror_children = g_list_prepend(spriv->mirror_children, dest); dpriv->mirror_parent = src; base_widget_style(dest); base_widget_update_value(dest); } } GtkWidget *base_widget_mirror ( GtkWidget *src ) { g_return_val_if_fail(IS_BASE_WIDGET(src),NULL); if(!BASE_WIDGET_GET_CLASS(src)->mirror) return NULL; return BASE_WIDGET_GET_CLASS(src)->mirror(src); } gboolean base_widget_emit_trigger ( gchar *trigger ) { BaseWidgetPrivate *priv; GList *iter; if(!trigger) return FALSE; g_debug("trigger: %s",trigger); scanner_invalidate(); g_mutex_lock(&widget_mutex); for(iter=widgets_scan;iter!=NULL;iter=g_list_next(iter)) { priv = base_widget_get_instance_private(BASE_WIDGET(iter->data)); if(!priv->trigger || g_ascii_strcasecmp(trigger,priv->trigger)) continue; if(expr_cache_eval(priv->value) || priv->always_update) base_widget_update_value(iter->data); if(expr_cache_eval(priv->style)) base_widget_style(iter->data); } g_mutex_unlock(&widget_mutex); action_exec(NULL, action_trigger_lookup(trigger), NULL, NULL, NULL); return FALSE; } gpointer base_widget_scanner_thread ( GMainContext *gmc ) { BaseWidgetPrivate *priv; GList *iter; gint64 timer, ctime; while ( TRUE ) { scanner_invalidate(); module_invalidate_all(); timer = G_MAXINT64; ctime = g_get_monotonic_time(); g_mutex_lock(&widget_mutex); for(iter=widgets_scan;iter!=NULL;iter=g_list_next(iter)) { priv = base_widget_get_instance_private(BASE_WIDGET(iter->data)); if(base_widget_get_next_poll(iter->data)<=ctime) { if(expr_cache_eval(priv->value) || priv->always_update) g_main_context_invoke(gmc,(GSourceFunc)base_widget_update_value, iter->data); if(expr_cache_eval(priv->style)) g_main_context_invoke(gmc,(GSourceFunc)base_widget_style, iter->data); base_widget_set_next_poll(iter->data,ctime); } timer = MIN(timer,base_widget_get_next_poll(iter->data)); } g_mutex_unlock(&widget_mutex); timer -= g_get_monotonic_time(); if(timer>0) g_usleep(timer); } } void base_widget_autoexec ( GtkWidget *self, gpointer data ) { action_t *action; if(GTK_IS_CONTAINER(self)) gtk_container_forall(GTK_CONTAINER(self),base_widget_autoexec, data); if(!IS_BASE_WIDGET(self)) return; action = base_widget_get_action(self,0,0); if(action) action_exec(self,action,NULL,wintree_from_id(wintree_get_focus()),NULL); } sfwbar-1.0~beta13/src/basewidget.h000066400000000000000000000064421450657570000171310ustar00rootroot00000000000000#ifndef __BASE_WIDGET_H__ #define __BASE_WIDGET_H__ #include "sfwbar.h" #include "action.h" #include "expr.h" #define BASE_WIDGET_TYPE (base_widget_get_type()) G_DECLARE_DERIVABLE_TYPE (BaseWidget, base_widget, BASE, WIDGET, GtkEventBox); #define IS_BASE_WIDGET BASE_IS_WIDGET typedef struct _BaseWidgetClass BaseWidgetClass; struct _BaseWidgetClass { GtkEventBoxClass parent_class; void (*update_value)(GtkWidget *self); void (*old_size_allocate)(GtkWidget *, GtkAllocation * ); GtkWidget *(*get_child)(GtkWidget *self); GtkWidget *(*mirror)(GtkWidget *self); gboolean (*action_exec)( GtkWidget *self, gint slot, GdkEvent *ev ); }; typedef struct _BaseWidgetPrivate BaseWidgetPrivate; struct _BaseWidgetPrivate { gchar *id; GList *css; ExprCache *style; ExprCache *value; ExprCache *tooltip; gulong tooltip_h; GList *actions; gulong button_h; gulong buttonp_h; gulong click_h; gulong scroll_h; gint64 interval; guint maxw, maxh; gchar *trigger; gint64 next_poll; gint dir; gboolean always_update; guint16 user_state; GdkRectangle rect; GList *mirror_children; GtkWidget *mirror_parent; GdkModifierType saved_modifiers; }; typedef struct _base_widget_attachment { action_t *action; gint event; GdkModifierType mods; } base_widget_attachment_t; GType base_widget_get_type ( void ); void base_widget_set_tooltip ( GtkWidget *self, gchar *tooltip ); void base_widget_set_value ( GtkWidget *self, gchar *value ); void base_widget_set_style ( GtkWidget *self, gchar *style ); void base_widget_set_trigger ( GtkWidget *self, gchar *trigger ); void base_widget_set_id ( GtkWidget *self, gchar *id ); void base_widget_set_interval ( GtkWidget *self, gint64 interval ); void base_widget_set_state ( GtkWidget *self, guint16 mask, gboolean state ); void base_widget_set_action ( GtkWidget *, gint, GdkModifierType, action_t *); void base_widget_set_max_width ( GtkWidget *self, guint x ); void base_widget_set_max_height ( GtkWidget *self, guint x ); gboolean base_widget_update_value ( GtkWidget *self ); void base_widget_set_rect ( GtkWidget *self, GdkRectangle rect ); void base_widget_attach ( GtkWidget *, GtkWidget *, GtkWidget *); guint16 base_widget_get_state ( GtkWidget *self ); gint64 base_widget_get_next_poll ( GtkWidget *self ); void base_widget_set_next_poll ( GtkWidget *self, gint64 ctime ); gchar *base_widget_get_id ( GtkWidget *self ); GtkWidget *base_widget_get_child ( GtkWidget *self ); GtkWidget *base_widget_from_id ( gchar *id ); gchar *base_widget_get_value ( GtkWidget *self ); action_t *base_widget_get_action ( GtkWidget *self, gint, GdkModifierType ); gpointer base_widget_scanner_thread ( GMainContext *gmc ); void base_widget_set_css ( GtkWidget *widget, gchar *css ); gboolean base_widget_emit_trigger ( gchar *trigger ); void base_widget_autoexec ( GtkWidget *self, gpointer data ); void base_widget_set_always_update ( GtkWidget *self, gboolean update ); void base_widget_copy_actions ( GtkWidget *dest, GtkWidget *src ); void base_widget_copy_properties ( GtkWidget *dest, GtkWidget *src ); GtkWidget *base_widget_mirror ( GtkWidget *src ); GdkModifierType base_widget_get_modifiers ( GtkWidget *self ); gboolean base_widget_action_exec ( GtkWidget *, gint, GdkEvent *); gboolean base_widget_check_action_slot ( GtkWidget *self, gint slot ); #endif sfwbar-1.0~beta13/src/button.c000066400000000000000000000031341450657570000163140ustar00rootroot00000000000000/* This entire file is licensed under GNU General Public License v3.0 * * Copyright 2022- sfwbar maintainers */ #include "sfwbar.h" #include "basewidget.h" #include "button.h" #include "scaleimage.h" G_DEFINE_TYPE_WITH_CODE (Button, button, BASE_WIDGET_TYPE, G_ADD_PRIVATE (Button)); static void button_update_value ( GtkWidget *self ) { ButtonPrivate *priv; g_return_if_fail(IS_BUTTON(self)); priv = button_get_instance_private(BUTTON(self)); scale_image_set_image(GTK_WIDGET(priv->image),base_widget_get_value(self),NULL); } static GtkWidget *button_get_child ( GtkWidget *self ) { ButtonPrivate *priv; g_return_val_if_fail(IS_BUTTON(self),NULL); priv = button_get_instance_private(BUTTON(self)); return priv->button; } static GtkWidget *button_mirror ( GtkWidget *src ) { GtkWidget *self; g_return_val_if_fail(IS_BUTTON(src),NULL); self = button_new(); base_widget_copy_properties(self,src); return self; } static void button_class_init ( ButtonClass *kclass ) { BASE_WIDGET_CLASS(kclass)->update_value = button_update_value; BASE_WIDGET_CLASS(kclass)->get_child = button_get_child; BASE_WIDGET_CLASS(kclass)->mirror = button_mirror; } static void button_init ( Button *self ) { } GtkWidget *button_new ( void ) { GtkWidget *self; ButtonPrivate *priv; self = GTK_WIDGET(g_object_new(button_get_type(), NULL)); priv = button_get_instance_private(BUTTON(self)); priv->button = gtk_button_new(); priv->image = scale_image_new(); gtk_container_add(GTK_CONTAINER(priv->button),priv->image); gtk_container_add(GTK_CONTAINER(self),priv->button); return self; } sfwbar-1.0~beta13/src/button.h000066400000000000000000000014521450657570000163220ustar00rootroot00000000000000#ifndef __BUTTON_H__ #define __BUTTON_H__ #include "basewidget.h" #define BUTTON_TYPE (button_get_type()) #define BUTTON(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), BUTTON_TYPE, Button)) #define BUTTON_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), BUTTON_TYPE, ButtonClass)) #define IS_BUTTON(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), BUTTON_TYPE)) #define IS_BUTTONCLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), BUTTON_TYPE)) typedef struct _Button Button; typedef struct _ButtonClass ButtonClass; struct _Button { BaseWidget item; }; struct _ButtonClass { BaseWidgetClass parent_class; }; typedef struct _ButtonPrivate ButtonPrivate; struct _ButtonPrivate { GtkWidget *button; GtkWidget *image; }; GType button_get_type ( void ); GtkWidget *button_new(); #endif sfwbar-1.0~beta13/src/cchart.c000066400000000000000000000030711450657570000162450ustar00rootroot00000000000000/* This entire file is licensed under GNU General Public License v3.0 * * Copyright 2022- sfwbar maintainers */ #include "cchart.h" #include "chart.h" G_DEFINE_TYPE_WITH_CODE (CChart, cchart, BASE_WIDGET_TYPE, G_ADD_PRIVATE (CChart)); static void cchart_update_value ( GtkWidget *self ) { CChartPrivate *priv; gchar *value; g_return_if_fail(IS_CCHART(self)); priv = cchart_get_instance_private(CCHART(self)); value = base_widget_get_value(self); if(!g_strrstr(value,"nan")) chart_update(priv->chart,g_ascii_strtod(value,NULL)); } static GtkWidget *cchart_get_child ( GtkWidget *self ) { CChartPrivate *priv; g_return_val_if_fail(IS_CCHART(self),NULL); priv = cchart_get_instance_private(CCHART(self)); return priv->chart; } static GtkWidget *cchart_mirror ( GtkWidget *src ) { GtkWidget *self; g_return_val_if_fail(IS_CCHART(src),NULL); self = cchart_new(); base_widget_copy_properties(self,src); return self; } static void cchart_class_init ( CChartClass *kclass ) { BASE_WIDGET_CLASS(kclass)->update_value = cchart_update_value; BASE_WIDGET_CLASS(kclass)->get_child = cchart_get_child; BASE_WIDGET_CLASS(kclass)->mirror = cchart_mirror; } static void cchart_init ( CChart *self ) { base_widget_set_always_update(GTK_WIDGET(self),TRUE); } GtkWidget *cchart_new ( void ) { GtkWidget *self; CChartPrivate *priv; self = GTK_WIDGET(g_object_new(cchart_get_type(), NULL)); priv = cchart_get_instance_private(CCHART(self)); priv->chart = chart_new(); gtk_container_add(GTK_CONTAINER(self),priv->chart); return self; } sfwbar-1.0~beta13/src/cchart.h000066400000000000000000000014251450657570000162530ustar00rootroot00000000000000#ifndef __CCHART_H__ #define __CCHART_H__ #include "basewidget.h" #define CCHART_TYPE (cchart_get_type()) #define CCHART(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), CCHART_TYPE, CChart)) #define CCHART_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), CCHART_TYPE, CChartClass)) #define IS_CCHART(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), CCHART_TYPE)) #define IS_CCHARTCLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), CCHART_TYPE)) typedef struct _CChart CChart; typedef struct _CChartClass CChartClass; struct _CChart { BaseWidget item; }; struct _CChartClass { BaseWidgetClass parent_class; }; typedef struct _CChartPrivate CChartPrivate; struct _CChartPrivate { GtkWidget *chart; }; GType cchart_get_type ( void ); GtkWidget *cchart_new(); #endif sfwbar-1.0~beta13/src/chart.c000066400000000000000000000064621450657570000161110ustar00rootroot00000000000000/* This entire file is licensed under GNU General Public License v3.0 * * Copyright 2022- sfwbar maintainers */ #include "sfwbar.h" #include "chart.h" #include "basewidget.h" G_DEFINE_TYPE_WITH_CODE (Chart, chart, GTK_TYPE_BOX, G_ADD_PRIVATE (Chart)); static void chart_destroy ( GtkWidget *self ) { ChartPrivate *priv; g_return_if_fail(IS_CHART(self)); priv = chart_get_instance_private(CHART(self)); g_queue_free(priv->data); GTK_WIDGET_CLASS(chart_parent_class)->destroy(self); } static gboolean chart_draw ( GtkWidget *self, cairo_t *cr ) { ChartPrivate *priv; GtkStyleContext *context; gint width,height; GtkBorder border,margin,padding,extents; GtkStateFlags flags; GdkRGBA fg; gdouble x_offset, y_offset; gint i, len; g_return_val_if_fail(IS_CHART(self),FALSE); priv = chart_get_instance_private(CHART(self)); width = gtk_widget_get_allocated_width(self); height = gtk_widget_get_allocated_height(self); context = gtk_widget_get_style_context(self); flags = gtk_style_context_get_state(context); gtk_style_context_get_border(context,flags,&border); gtk_style_context_get_padding(context,flags,&padding); gtk_style_context_get_margin(context,flags,&margin); extents.left = border.left + margin.left + padding.left; extents.right = border.right + margin.right + padding.right; extents.top = border.top + margin.top + padding.top; extents.bottom = border.bottom + margin.bottom + padding.bottom; gtk_render_background(context,cr,extents.left,extents.top, width - extents.left - extents.right, height - extents.top - extents.bottom); gtk_render_frame(context,cr,margin.left, margin.top, width - margin.left - margin.right, height - margin.top - margin.bottom); width = width - extents.left - extents.right; height = height - extents.top - extents.bottom - 1; if( width<1 || height<1 ) return FALSE; while(g_queue_get_length(priv->data)>width) g_free(g_queue_pop_head(priv->data)); len = g_queue_get_length(priv->data); x_offset = width + extents.left - len + 0.5; y_offset = height + extents.top + 0.5; gtk_style_context_get_color (context,flags, &fg); cairo_set_source_rgba(cr,fg.red,fg.green,fg.blue,fg.alpha); cairo_set_line_width(cr,1); cairo_move_to(cr,x_offset,y_offset); for(i=0;idata,i)); cairo_line_to(cr,x_offset + len - 1, y_offset); cairo_close_path(cr); cairo_stroke_preserve(cr); cairo_fill(cr); return TRUE; } static void chart_class_init ( ChartClass *kclass ) { GtkWidgetClass *widget_class = GTK_WIDGET_CLASS(kclass); gtk_widget_class_set_css_name(GTK_WIDGET_CLASS(kclass),"chart"); widget_class->destroy = chart_destroy; widget_class->draw = chart_draw; } static void chart_init ( Chart *self ) { ChartPrivate *priv; g_return_if_fail(IS_CHART(self)); priv = chart_get_instance_private(CHART(self)); priv->data = g_queue_new(); } GtkWidget *chart_new( void ) { return GTK_WIDGET(g_object_new(chart_get_type(), NULL)); } int chart_update ( GtkWidget *self, gdouble n ) { ChartPrivate *priv; g_return_val_if_fail(IS_CHART(self),0); priv = chart_get_instance_private(CHART(self)); g_queue_push_tail(priv->data,g_memdup(&n,sizeof(double))); gtk_widget_queue_draw(self); return 0; } sfwbar-1.0~beta13/src/chart.h000066400000000000000000000015061450657570000161100ustar00rootroot00000000000000#ifndef __CHART_H__ #define __CHART_H__ #include "basewidget.h" #define CHART_TYPE (chart_get_type()) #define CHART(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), CHART_TYPE, Chart)) #define CHART_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), CHART_TYPE, ChartClass)) #define IS_CHART(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), CHART_TYPE)) #define IS_CHART_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), CHART_TYPE)) typedef struct _Chart Chart; typedef struct _ChartClass ChartClass; struct _Chart { GtkBox parent_class; }; struct _ChartClass { GtkBoxClass parent_class; }; typedef struct _ChartPrivate ChartPrivate; struct _ChartPrivate { GQueue *data; GtkWidget *chart; }; GType chart_get_type ( void ); GtkWidget *chart_new( void ); int chart_update ( GtkWidget *widget, gdouble n ); #endif sfwbar-1.0~beta13/src/client.c000066400000000000000000000131151450657570000162570ustar00rootroot00000000000000/* This entire file is licensed under GNU General Public License v3.0 * * Copyright 2022- sfwbar maintainers */ #include #include #include #include "sfwbar.h" #include "basewidget.h" #include "client.h" void client_reconnect ( Client *client ) { g_debug("client %s: disconnecting",client->file->fname); if(client->in == client->out) client->out = NULL; g_clear_pointer(&client->in,g_io_channel_unref); g_clear_pointer(&client->out,g_io_channel_unref); g_clear_pointer(&client->scon,g_object_unref); g_clear_pointer(&client->sclient,g_object_unref); g_clear_pointer(&client->addr,g_object_unref); if(client->connect) g_timeout_add(1000, (GSourceFunc )client->connect, client); } gboolean client_event ( GIOChannel *chan, GIOCondition cond, gpointer data ) { Client *client = data; gsize size; GIOStatus cstat; g_debug("client %s: event fd %d, flags %d, cond %d",client->file->fname, g_io_channel_unix_get_fd(chan),g_io_channel_get_flags(chan),cond); if( cond & G_IO_ERR || cond & G_IO_HUP ) { g_debug("client %s: error cond = %d",client->file->fname,cond); return FALSE; } if( cond & G_IO_IN || cond & G_IO_PRI ) { if(client->consume) cstat = client->consume(client, &size); else { g_list_foreach(client->file->vars,(GFunc)scanner_var_reset,NULL); cstat = scanner_file_update( chan, client->file, &size ); } if(cstat == G_IO_STATUS_ERROR || !size ) { g_debug("client %s: read error, status = %d, size = %ld", client->file->fname,cstat,size); return FALSE; } else g_debug("client %s: status %d, read %ld bytes", client->file->fname,cstat,size); } if(client->respond) { cstat = client->respond(client); if(cstat != G_IO_STATUS_NORMAL) { g_debug("client %s: write error, status = %d",client->file->fname,cstat); client_reconnect(client); return FALSE; } } base_widget_emit_trigger(client->file->trigger); return TRUE; } void client_attach ( Client *client ) { scanner_file_attach(client->file->trigger,client->file); client->connect(client); } void client_subscribe ( Client *client ) { if(client->out && client->out != client->in) { g_io_channel_set_flags(client->out,G_IO_FLAG_NONBLOCK,NULL); g_io_channel_set_close_on_unref(client->out,TRUE); } if(client->in) { g_io_channel_set_flags(client->in,G_IO_FLAG_NONBLOCK,NULL); g_io_channel_set_close_on_unref(client->in,TRUE); g_io_add_watch_full(client->in,G_PRIORITY_DEFAULT, G_IO_IN | G_IO_PRI | G_IO_HUP | G_IO_ERR, client_event, client, (GDestroyNotify)client_reconnect); g_debug("client %s: connected, channel: %p, fd: %d, flags: %d, conn: %p", client->file->fname,client->out,g_io_channel_unix_get_fd(client->out), g_io_channel_get_flags(client->out),client->scon); } } void client_socket_connect_cb ( GSocketClient *src, GAsyncResult *res, Client *client ) { GSocket *sock; client->scon = g_socket_client_connect_finish(src,res,NULL); if(client->scon && g_socket_connection_is_connected(client->scon)) { sock = g_socket_connection_get_socket(client->scon); if(sock) { g_socket_set_keepalive(sock,TRUE); client->out = g_io_channel_unix_new(g_socket_get_fd(sock)); if(client->out) { client->in = client->out; client_subscribe(client); return; } } } g_debug("client: %s: socket connection failed",client->file->fname); client_reconnect(client); } gboolean client_socket_connect ( Client *client ) { g_debug("client %s: connecting",client->file->fname); if(strchr(client->file->fname,':')) client->addr = g_network_address_parse(client->file->fname,0,NULL); else client->addr = (GSocketConnectable*) g_unix_socket_address_new(client->file->fname); if(!client->addr) { g_debug("client %s: unable to parse address",client->file->fname); client_reconnect(client); return FALSE; } client->sclient = g_socket_client_new(); g_socket_client_connect_async(client->sclient,client->addr,NULL, (GAsyncReadyCallback)client_socket_connect_cb,client); return FALSE; } void client_socket ( ScanFile *file ) { Client *client; if( !file || !file->fname ) return; client = g_malloc0(sizeof(Client)); client->file = file; client->connect = client_socket_connect; file->client = client; client_attach(client); } gboolean client_exec_connect ( Client *client ) { gint in,out,err; gchar **argv; gint argc; if(!g_shell_parse_argv(client->file->fname,&argc,&argv,NULL)) return FALSE; if(!g_spawn_async_with_pipes(NULL,argv,NULL,G_SPAWN_SEARCH_PATH,NULL,NULL,NULL, &in, &out, &err, NULL)) { g_debug("client exec error on: %s",client->file->fname); g_strfreev(argv); return FALSE; } g_strfreev(argv); client->in = g_io_channel_unix_new(out); client->out = g_io_channel_unix_new(in); client_subscribe(client); return FALSE; } void client_exec ( ScanFile *file ) { Client *client; if( !file || !file->fname ) return; client = g_malloc0(sizeof(Client)); client->file = file; client->connect = client_exec_connect; client_attach(client); file->client = client; } void client_send ( gchar *addr, gchar *command ) { ScanFile *file; Client *client; if(!addr || !command ) return; file = scanner_file_get ( addr ); if(!file) return; client = file->client; if(!client || !client->out) return; (void)g_io_channel_write_chars(client->out, command, -1, NULL, NULL); g_io_channel_flush(client->out, NULL); } sfwbar-1.0~beta13/src/client.h000066400000000000000000000012751450657570000162700ustar00rootroot00000000000000#ifndef __CLIENT_H__ #define __CLIENT_H__ #include #include "scanner.h" typedef struct _Client Client; struct _Client { ScanFile *file; GSocketConnection *scon; GSocketConnectable *addr; GSocketClient *sclient; GIOChannel *in,*out; void *data; gboolean (*connect) ( Client * ); GIOStatus (*respond) ( Client * ); GIOStatus (*consume) ( Client *, gsize *size ); }; void client_exec ( ScanFile *file ); void client_socket ( ScanFile *file ); void client_mpd ( ScanFile *file ); void client_attach ( Client *client ); gboolean client_socket_connect ( Client *client ); void client_send ( gchar *addr, gchar *command ); void client_mpd_command ( gchar *command ); #endif sfwbar-1.0~beta13/src/config.h000066400000000000000000000065501450657570000162600ustar00rootroot00000000000000#ifndef __CONFIG_H__ #define __CONFIG_H__ #include #include "action.h" enum ConfigSequenceType { SEQ_OPT, SEQ_CON, SEQ_REQ, SEQ_END }; typedef gboolean (*parse_func) ( GScanner *, void * ); gchar *config_value_string ( gchar *dest, gchar *string ); GtkWidget *config_parse ( gchar *, gboolean ); void config_pipe_read ( gchar *command ); void config_string ( gchar *string ); gboolean config_expect_token ( GScanner *scan, gint token, gchar *fmt, ...); void config_optional_semicolon ( GScanner *scanner ); void config_parse_sequence ( GScanner *scanner, ... ); gboolean config_assign_boolean (GScanner *scanner, gboolean def, gchar *expr); gchar *config_assign_string ( GScanner *scanner, gchar *expr ); gdouble config_assign_number ( GScanner *scanner, gchar *expr ); gint config_assign_tokens ( GScanner *scanner, gchar *, gchar *, ... ); action_t *config_action ( GScanner *scanner ); void config_action_finish ( GScanner *scanner ); gchar *config_get_value ( GScanner *, gchar *, gboolean, gchar **); void config_scanner ( GScanner *scanner ); void config_layout ( GScanner *, GtkWidget **, gboolean ); GtkWidget *config_include ( GScanner *scanner, gboolean toplevel ); void config_switcher ( GScanner *scanner ); void config_placer ( GScanner *scanner ); void config_popup ( GScanner *scanner ); GtkWidget *config_parse_toplevel ( GScanner *scanner, gboolean toplevel ); enum { G_TOKEN_SCANNER = G_TOKEN_LAST + 50, G_TOKEN_LAYOUT, G_TOKEN_POPUP, G_TOKEN_PLACER, G_TOKEN_SWITCHER, G_TOKEN_DEFINE, G_TOKEN_TRIGGERACTION, G_TOKEN_MAPAPPID, G_TOKEN_FILTERAPPID, G_TOKEN_FILTERTITLE, G_TOKEN_MODULE, G_TOKEN_THEME, G_TOKEN_DISOWNMINIMIZED, G_TOKEN_END, G_TOKEN_FILE, G_TOKEN_EXEC, G_TOKEN_MPDCLIENT, G_TOKEN_SWAYCLIENT, G_TOKEN_EXECCLIENT, G_TOKEN_SOCKETCLIENT, G_TOKEN_NUMBERW, G_TOKEN_STRINGW, G_TOKEN_NOGLOB, G_TOKEN_CHTIME, G_TOKEN_SUM, G_TOKEN_PRODUCT, G_TOKEN_LASTW, G_TOKEN_FIRST, G_TOKEN_GRID, G_TOKEN_SCALE, G_TOKEN_LABEL, G_TOKEN_BUTTON, G_TOKEN_IMAGE, G_TOKEN_CHART, G_TOKEN_INCLUDE, G_TOKEN_TASKBAR, G_TOKEN_PAGER, G_TOKEN_TRAY, G_TOKEN_STYLE, G_TOKEN_CSS, G_TOKEN_INTERVAL, G_TOKEN_VALUE, G_TOKEN_PINS, G_TOKEN_PREVIEW, G_TOKEN_COLS, G_TOKEN_ROWS, G_TOKEN_ACTION, G_TOKEN_DISPLAY, G_TOKEN_ICONS, G_TOKEN_LABELS, G_TOKEN_LOC, G_TOKEN_NUMERIC, G_TOKEN_PEROUTPUT, G_TOKEN_TITLEWIDTH, G_TOKEN_TOOLTIP, G_TOKEN_TRIGGER, G_TOKEN_GROUP, G_TOKEN_XSTEP, G_TOKEN_YSTEP, G_TOKEN_XORIGIN, G_TOKEN_YORIGIN, G_TOKEN_CHILDREN, G_TOKEN_SORT, G_TOKEN_FILTER, G_TOKEN_PRIMARY, G_TOKEN_TRUE, G_TOKEN_FALSE, G_TOKEN_MENU, G_TOKEN_AUTOCLOSE, G_TOKEN_MENUCLEAR, G_TOKEN_FUNCTION, G_TOKEN_USERSTATE, G_TOKEN_USERSTATE2, G_TOKEN_CLIENTSEND, G_TOKEN_ITEM, G_TOKEN_SEPARATOR, G_TOKEN_SUBMENU, G_TOKEN_MAXIMIZED, G_TOKEN_MINIMIZED, G_TOKEN_FULLSCREEN, G_TOKEN_FOCUSED, G_TOKEN_REGEX, G_TOKEN_JSON, G_TOKEN_SET, G_TOKEN_GRAB, G_TOKEN_TITLE, G_TOKEN_APPID, G_TOKEN_WORKSPACE, G_TOKEN_OUTPUT, G_TOKEN_FLOATING, G_TOKEN_INIT, G_TOKEN_LEFT, G_TOKEN_MIDDLE, G_TOKEN_RIGHT, G_TOKEN_SCROLL_UP, G_TOKEN_SCROLL_LEFT, G_TOKEN_SCROLL_RIGHT, G_TOKEN_SCROLL_DOWN, G_TOKEN_SHIFT, G_TOKEN_CTRL, G_TOKEN_MOD1, G_TOKEN_MOD2, G_TOKEN_MOD3, G_TOKEN_MOD4, G_TOKEN_MOD5, G_TOKEN_SUPER, G_TOKEN_HYPER, G_TOKEN_META }; #endif sfwbar-1.0~beta13/src/config/000077500000000000000000000000001450657570000161015ustar00rootroot00000000000000sfwbar-1.0~beta13/src/config/base.c000066400000000000000000000116411450657570000171620ustar00rootroot00000000000000/* This entire file is licensed under GNU General Public License v3.0 * * Copyright 2022- sfwbar maintainers */ #include "../config.h" gboolean config_expect_token ( GScanner *scanner, gint token, gchar *fmt, ...) { gchar *errmsg; va_list args; if( g_scanner_peek_next_token(scanner) == token ) return TRUE; va_start(args,fmt); errmsg = g_strdup_vprintf(fmt,args); va_end(args); g_scanner_error(scanner,"%s",errmsg); g_free(errmsg); return FALSE; } void config_optional_semicolon ( GScanner *scanner ) { if(g_scanner_peek_next_token(scanner) == ';') g_scanner_get_next_token(scanner); } void config_parse_sequence ( GScanner *scanner, ... ) { va_list args; void *dest; parse_func func; gchar *err; gint type; gint req; gboolean matched = TRUE; scanner->max_parse_errors = FALSE; va_start(args,scanner); req = va_arg(args, gint ); while(req!=SEQ_END) { type = va_arg(args, gint ); func = va_arg(args, parse_func ); dest = va_arg(args, void * ); err = va_arg(args, char * ); if( (matched || req != SEQ_CON) && ( type < 0 || type==G_TOKEN_VALUE || g_scanner_peek_next_token(scanner) == type || (scanner->next_token == G_TOKEN_FLOAT && type == G_TOKEN_INT) )) { if(type == -2) { if(func && !func(scanner,dest)) if(err) g_scanner_error(scanner,"%s",err); } else if(type != G_TOKEN_VALUE) g_scanner_get_next_token(scanner); matched = TRUE; if(dest) switch(type) { case G_TOKEN_STRING: *((gchar **)dest) = g_strdup(scanner->value.v_string); break; case G_TOKEN_IDENTIFIER: *((gchar **)dest) = g_strdup(scanner->value.v_identifier); break; case G_TOKEN_VALUE: *((gchar **)dest) = config_get_value(scanner,err,FALSE,NULL); if(!**((gchar **)dest)) { g_free(*((gchar **)dest)); *((char **)dest)=NULL; } break; case G_TOKEN_FLOAT: *((gdouble *)dest) = scanner->value.v_float; break; case G_TOKEN_INT: *((gint *)dest) = (gint)scanner->value.v_float; break; case -1: *((gint *)dest) = scanner->token; break; case -2: break; default: *((gboolean *)dest) = TRUE; break; } } else if(req == SEQ_OPT || (req == SEQ_CON && !matched)) matched = FALSE; else g_scanner_error(scanner,"%s",err); req = va_arg(args, gint ); } va_end(args); } gboolean config_assign_boolean (GScanner *scanner, gboolean def, gchar *expr) { gboolean result = def; scanner->max_parse_errors = FALSE; if(!config_expect_token(scanner, '=', "Missing '=' in %s = ",expr)) return FALSE; g_scanner_get_next_token(scanner); switch((gint)g_scanner_get_next_token(scanner)) { case G_TOKEN_TRUE: result = TRUE; break; case G_TOKEN_FALSE: result = FALSE; break; default: g_scanner_error(scanner, "Missing value in %s = ", expr); break; } config_optional_semicolon(scanner); return result; } gchar *config_assign_string ( GScanner *scanner, gchar *expr ) { gchar *result; scanner->max_parse_errors = FALSE; if(!config_expect_token(scanner, '=', "Missing '=' in %s = ",expr)) return NULL; g_scanner_get_next_token(scanner); if(!config_expect_token(scanner, G_TOKEN_STRING, "Missing in %s = ",expr)) return NULL; g_scanner_get_next_token(scanner); result = g_strdup(scanner->value.v_string); config_optional_semicolon(scanner); return result; } gdouble config_assign_number ( GScanner *scanner, gchar *expr ) { gdouble result; scanner->max_parse_errors = FALSE; if(!config_expect_token(scanner, '=', "Missing '=' in %s = ",expr)) return 0; g_scanner_get_next_token(scanner); if(!config_expect_token(scanner, G_TOKEN_FLOAT, "Missing in %s = ",expr)) return 0; g_scanner_get_next_token(scanner); result = scanner->value.v_float; config_optional_semicolon(scanner); return result; } gint config_assign_tokens ( GScanner *scanner, gchar *name, gchar *type, ... ) { va_list args; gint token, res = 0; scanner->max_parse_errors = FALSE; if(!config_expect_token(scanner, '=', "Missing '=' in %s = %s",name,type)) return 0; g_scanner_get_next_token(scanner); g_scanner_peek_next_token(scanner); va_start(args,type); token = va_arg(args, gint ); while(token!=0) { if(token == scanner->next_token) res = g_scanner_get_next_token(scanner); token = va_arg(args, gint ); } va_end(args); if(!res) g_scanner_error(scanner,"missing %s in %s = %s",name,type,name); config_optional_semicolon(scanner); return res; } sfwbar-1.0~beta13/src/config/init.c000066400000000000000000000272341450657570000172200ustar00rootroot00000000000000/* This entire file is licensed under GNU General Public License v3.0 * * Copyright 2022- sfwbar maintainers */ #include "../config.h" #include "../sfwbar.h" void config_log_error ( GScanner *scanner, gchar *message, gboolean error ) { if(error) { if(!scanner->max_parse_errors) g_message("%s:%d: %s",scanner->input_name,scanner->line,message); scanner->max_parse_errors = TRUE; } else g_message("%s:%d: %s",scanner->input_name,scanner->line,message); } GtkWidget *config_parse_data ( gchar *fname, gchar *data, gboolean toplevel ) { GScanner *scanner; GtkWidget *w; GtkCssProvider *css; gchar *tmp; if(!data) return NULL; scanner = g_scanner_new(NULL); scanner->config->scan_octal = 0; scanner->config->symbol_2_token = 1; scanner->config->case_sensitive = 0; scanner->config->numbers_2_int = 1; scanner->config->int_2_float = 1; scanner->config->scope_0_fallback = 0; scanner->config->cset_identifier_nth = g_strconcat(".", scanner->config->cset_identifier_nth,NULL); scanner->config->cset_identifier_first = g_strconcat("$", scanner->config->cset_identifier_first,NULL); scanner->msg_handler = config_log_error; scanner->max_parse_errors = FALSE; g_scanner_scope_add_symbol(scanner,0, "Scanner", (gpointer)G_TOKEN_SCANNER ); g_scanner_scope_add_symbol(scanner,0, "Layout", (gpointer)G_TOKEN_LAYOUT ); g_scanner_scope_add_symbol(scanner,0, "PopUp", (gpointer)G_TOKEN_POPUP ); g_scanner_scope_add_symbol(scanner,0, "Placer", (gpointer)G_TOKEN_PLACER ); g_scanner_scope_add_symbol(scanner,0, "Switcher", (gpointer)G_TOKEN_SWITCHER ); g_scanner_scope_add_symbol(scanner,0, "Define", (gpointer)G_TOKEN_DEFINE ); g_scanner_scope_add_symbol(scanner,0, "TriggerAction", (gpointer)G_TOKEN_TRIGGERACTION ); g_scanner_scope_add_symbol(scanner,0, "MapAppId", (gpointer)G_TOKEN_MAPAPPID ); g_scanner_scope_add_symbol(scanner,0, "FilterAppId", (gpointer)G_TOKEN_FILTERAPPID ); g_scanner_scope_add_symbol(scanner,0, "FilterTitle", (gpointer)G_TOKEN_FILTERTITLE ); g_scanner_scope_add_symbol(scanner,0, "Module", (gpointer)G_TOKEN_MODULE ); g_scanner_scope_add_symbol(scanner,0, "Theme", (gpointer)G_TOKEN_THEME ); g_scanner_scope_add_symbol(scanner,0, "DisownMinimized", (gpointer)G_TOKEN_DISOWNMINIMIZED ); g_scanner_scope_add_symbol(scanner,0, "End", (gpointer)G_TOKEN_END ); g_scanner_scope_add_symbol(scanner,0, "File", (gpointer)G_TOKEN_FILE ); g_scanner_scope_add_symbol(scanner,0, "Exec", (gpointer)G_TOKEN_EXEC ); g_scanner_scope_add_symbol(scanner,0, "MpdClient", (gpointer)G_TOKEN_MPDCLIENT ); g_scanner_scope_add_symbol(scanner,0, "SwayClient", (gpointer)G_TOKEN_SWAYCLIENT ); g_scanner_scope_add_symbol(scanner,0, "ExecClient", (gpointer)G_TOKEN_EXECCLIENT ); g_scanner_scope_add_symbol(scanner,0, "SocketClient", (gpointer)G_TOKEN_SOCKETCLIENT ); g_scanner_scope_add_symbol(scanner,0, "Number", (gpointer)G_TOKEN_NUMBERW ); g_scanner_scope_add_symbol(scanner,0, "String", (gpointer)G_TOKEN_STRINGW ); g_scanner_scope_add_symbol(scanner,0, "NoGlob", (gpointer)G_TOKEN_NOGLOB ); g_scanner_scope_add_symbol(scanner,0, "CheckTime", (gpointer)G_TOKEN_CHTIME ); g_scanner_scope_add_symbol(scanner,0, "Sum", (gpointer)G_TOKEN_SUM ); g_scanner_scope_add_symbol(scanner,0, "Product", (gpointer)G_TOKEN_PRODUCT ); g_scanner_scope_add_symbol(scanner,0, "Last", (gpointer)G_TOKEN_LASTW ); g_scanner_scope_add_symbol(scanner,0, "First", (gpointer)G_TOKEN_FIRST ); g_scanner_scope_add_symbol(scanner,0, "Grid", (gpointer)G_TOKEN_GRID ); g_scanner_scope_add_symbol(scanner,0, "Scale", (gpointer)G_TOKEN_SCALE ); g_scanner_scope_add_symbol(scanner,0, "Label", (gpointer)G_TOKEN_LABEL ); g_scanner_scope_add_symbol(scanner,0, "Button", (gpointer)G_TOKEN_BUTTON ); g_scanner_scope_add_symbol(scanner,0, "Image", (gpointer)G_TOKEN_IMAGE ); g_scanner_scope_add_symbol(scanner,0, "Chart", (gpointer)G_TOKEN_CHART ); g_scanner_scope_add_symbol(scanner,0, "Include", (gpointer)G_TOKEN_INCLUDE ); g_scanner_scope_add_symbol(scanner,0, "TaskBar", (gpointer)G_TOKEN_TASKBAR ); g_scanner_scope_add_symbol(scanner,0, "Pager", (gpointer)G_TOKEN_PAGER ); g_scanner_scope_add_symbol(scanner,0, "Tray", (gpointer)G_TOKEN_TRAY ); g_scanner_scope_add_symbol(scanner,0, "Style", (gpointer)G_TOKEN_STYLE ); g_scanner_scope_add_symbol(scanner,0, "Css", (gpointer)G_TOKEN_CSS ); g_scanner_scope_add_symbol(scanner,0, "Interval", (gpointer)G_TOKEN_INTERVAL ); g_scanner_scope_add_symbol(scanner,0, "Value", (gpointer)G_TOKEN_VALUE ); g_scanner_scope_add_symbol(scanner,0, "Pins", (gpointer)G_TOKEN_PINS ); g_scanner_scope_add_symbol(scanner,0, "Preview", (gpointer)G_TOKEN_PREVIEW ); g_scanner_scope_add_symbol(scanner,0, "Cols", (gpointer)G_TOKEN_COLS ); g_scanner_scope_add_symbol(scanner,0, "Rows", (gpointer)G_TOKEN_ROWS ); g_scanner_scope_add_symbol(scanner,0, "Action", (gpointer)G_TOKEN_ACTION ); g_scanner_scope_add_symbol(scanner,0, "Display", (gpointer)G_TOKEN_DISPLAY ); g_scanner_scope_add_symbol(scanner,0, "Icons", (gpointer)G_TOKEN_ICONS ); g_scanner_scope_add_symbol(scanner,0, "Labels", (gpointer)G_TOKEN_LABELS ); g_scanner_scope_add_symbol(scanner,0, "Loc", (gpointer)G_TOKEN_LOC ); g_scanner_scope_add_symbol(scanner,0, "Numeric", (gpointer)G_TOKEN_NUMERIC ); g_scanner_scope_add_symbol(scanner,0, "Filter_output", (gpointer)G_TOKEN_PEROUTPUT ); g_scanner_scope_add_symbol(scanner,0, "Title_width", (gpointer)G_TOKEN_TITLEWIDTH ); g_scanner_scope_add_symbol(scanner,0, "Tooltip", (gpointer)G_TOKEN_TOOLTIP ); g_scanner_scope_add_symbol(scanner,0, "Trigger", (gpointer)G_TOKEN_TRIGGER ); g_scanner_scope_add_symbol(scanner,0, "Group", (gpointer)G_TOKEN_GROUP ); g_scanner_scope_add_symbol(scanner,0, "XStep", (gpointer)G_TOKEN_XSTEP ); g_scanner_scope_add_symbol(scanner,0, "YStep", (gpointer)G_TOKEN_YSTEP ); g_scanner_scope_add_symbol(scanner,0, "XOrigin", (gpointer)G_TOKEN_XORIGIN ); g_scanner_scope_add_symbol(scanner,0, "YOrigin", (gpointer)G_TOKEN_YORIGIN ); g_scanner_scope_add_symbol(scanner,0, "Children", (gpointer)G_TOKEN_CHILDREN ); g_scanner_scope_add_symbol(scanner,0, "Sort", (gpointer)G_TOKEN_SORT ); g_scanner_scope_add_symbol(scanner,0, "Filter", (gpointer)G_TOKEN_FILTER ); g_scanner_scope_add_symbol(scanner,0, "Primary", (gpointer)G_TOKEN_PRIMARY ); g_scanner_scope_add_symbol(scanner,0, "True", (gpointer)G_TOKEN_TRUE ); g_scanner_scope_add_symbol(scanner,0, "False", (gpointer)G_TOKEN_FALSE ); g_scanner_scope_add_symbol(scanner,0, "Menu", (gpointer)G_TOKEN_MENU ); g_scanner_scope_add_symbol(scanner,0, "MenuClear", (gpointer)G_TOKEN_MENUCLEAR ); g_scanner_scope_add_symbol(scanner,0, "UserState", (gpointer)G_TOKEN_USERSTATE ); g_scanner_scope_add_symbol(scanner,0, "UserState2", (gpointer)G_TOKEN_USERSTATE2 ); g_scanner_scope_add_symbol(scanner,0, "Function", (gpointer)G_TOKEN_FUNCTION ); g_scanner_scope_add_symbol(scanner,0, "Item", (gpointer)G_TOKEN_ITEM ); g_scanner_scope_add_symbol(scanner,0, "Separator", (gpointer)G_TOKEN_SEPARATOR ); g_scanner_scope_add_symbol(scanner,0, "SubMenu", (gpointer)G_TOKEN_SUBMENU ); g_scanner_scope_add_symbol(scanner,0, "AutoClose", (gpointer)G_TOKEN_AUTOCLOSE ); g_scanner_scope_add_symbol(scanner,0, "Minimized", (gpointer)G_TOKEN_MINIMIZED ); g_scanner_scope_add_symbol(scanner,0, "Maximized", (gpointer)G_TOKEN_MAXIMIZED ); g_scanner_scope_add_symbol(scanner,0, "FullScreen", (gpointer)G_TOKEN_FULLSCREEN ); g_scanner_scope_add_symbol(scanner,0, "Focused", (gpointer)G_TOKEN_FOCUSED ); g_scanner_scope_add_symbol(scanner,0, "RegEx", (gpointer)G_TOKEN_REGEX ); g_scanner_scope_add_symbol(scanner,0, "Json", (gpointer)G_TOKEN_JSON ); g_scanner_scope_add_symbol(scanner,0, "Set", (gpointer)G_TOKEN_SET ); g_scanner_scope_add_symbol(scanner,0, "Grab", (gpointer)G_TOKEN_GRAB ); g_scanner_scope_add_symbol(scanner,0, "Title", (gpointer)G_TOKEN_TITLE ); g_scanner_scope_add_symbol(scanner,0, "AppId", (gpointer)G_TOKEN_APPID ); g_scanner_scope_add_symbol(scanner,0, "Workspace", (gpointer)G_TOKEN_WORKSPACE ); g_scanner_scope_add_symbol(scanner,0, "Output", (gpointer)G_TOKEN_OUTPUT ); g_scanner_scope_add_symbol(scanner,0, "Floating", (gpointer)G_TOKEN_FLOATING ); g_scanner_scope_add_symbol(scanner,0, "Init", (gpointer)G_TOKEN_INIT ); g_scanner_scope_add_symbol(scanner,0, "LeftClick", (gpointer)G_TOKEN_LEFT ); g_scanner_scope_add_symbol(scanner,0, "MiddleClick", (gpointer)G_TOKEN_MIDDLE ); g_scanner_scope_add_symbol(scanner,0, "RightClick", (gpointer)G_TOKEN_RIGHT ); g_scanner_scope_add_symbol(scanner,0, "ScrollUp", (gpointer)G_TOKEN_SCROLL_UP ); g_scanner_scope_add_symbol(scanner,0, "ScrollLeft", (gpointer)G_TOKEN_SCROLL_LEFT ); g_scanner_scope_add_symbol(scanner,0, "ScrollRight", (gpointer)G_TOKEN_SCROLL_RIGHT ); g_scanner_scope_add_symbol(scanner,0, "ScrollDown", (gpointer)G_TOKEN_SCROLL_DOWN ); g_scanner_scope_add_symbol(scanner,0, "Shift", (gpointer)G_TOKEN_SHIFT ); g_scanner_scope_add_symbol(scanner,0, "Ctrl", (gpointer)G_TOKEN_CTRL ); g_scanner_scope_add_symbol(scanner,0, "Mod1", (gpointer)G_TOKEN_MOD1 ); g_scanner_scope_add_symbol(scanner,0, "Mod2", (gpointer)G_TOKEN_MOD2 ); g_scanner_scope_add_symbol(scanner,0, "Mod3", (gpointer)G_TOKEN_MOD3 ); g_scanner_scope_add_symbol(scanner,0, "Mod4", (gpointer)G_TOKEN_MOD4 ); g_scanner_scope_add_symbol(scanner,0, "Mod5", (gpointer)G_TOKEN_MOD5 ); g_scanner_scope_add_symbol(scanner,0, "Super", (gpointer)G_TOKEN_SUPER ); g_scanner_scope_add_symbol(scanner,0, "Hyper", (gpointer)G_TOKEN_HYPER ); g_scanner_scope_add_symbol(scanner,0, "Meta", (gpointer)G_TOKEN_META ); tmp = strstr(data,"\n#CSS"); if(tmp) { *tmp=0; css = gtk_css_provider_new(); gtk_css_provider_load_from_data(css,tmp+5,strlen(tmp+5),NULL); gtk_style_context_add_provider_for_screen(gdk_screen_get_default(), GTK_STYLE_PROVIDER(css),GTK_STYLE_PROVIDER_PRIORITY_USER); g_object_unref(css); } scanner->input_name = fname; g_scanner_input_text( scanner, data, -1 ); w = config_parse_toplevel ( scanner, toplevel ); g_free(scanner->config->cset_identifier_first); g_free(scanner->config->cset_identifier_nth); g_scanner_destroy(scanner); return w; } void config_string ( gchar *string ) { gchar *conf; if(!string) return; conf = g_strdup(string); config_parse_data("config string",conf,TRUE); g_free(conf); } void config_pipe_read ( gchar *command ) { FILE *fp; gchar *conf; GIOChannel *chan; if(!command) return; fp = popen(command, "r"); if(!fp) return; chan = g_io_channel_unix_new( fileno(fp) ); if(chan) { if(g_io_channel_read_to_end( chan , &conf,NULL,NULL)==G_IO_STATUS_NORMAL) config_parse_data(command,conf,TRUE); g_free(conf); g_io_channel_unref(chan); } pclose(fp); } GtkWidget *config_parse ( gchar *file, gboolean toplevel ) { gchar *fname, *dir, *base ,*cssfile, *csspath, *tmp; gchar *conf=NULL; gsize size; GtkWidget *w=NULL; fname = get_xdg_config_file(file,NULL); g_debug("include: %s -> %s",file,fname); if(fname) if(!g_file_get_contents(fname,&conf,&size,NULL)) conf=NULL; if(!conf) { g_error("Error: can't read config file %s\n",file); exit(1); } w = config_parse_data (fname, conf, toplevel); g_free(conf); dir = g_path_get_dirname(fname); base = g_path_get_basename(fname); tmp = strrchr(base,'.'); if(tmp) *tmp = '\0'; cssfile = g_strconcat(base,".css",NULL); csspath = g_build_filename(dir,cssfile,NULL); css_file_load (csspath); g_free(csspath); g_free(cssfile); g_free(base); g_free(dir); g_free(fname); return w; } sfwbar-1.0~beta13/src/config/layout.c000066400000000000000000000343421450657570000175700ustar00rootroot00000000000000/* This entire file is licensed under GNU General Public License v3.0 * * Copyright 2022- sfwbar maintainers */ #include "../config.h" #include "../sfwbar.h" #include "../bar.h" #include "../button.h" #include "../scale.h" #include "../image.h" #include "../label.h" #include "../cchart.h" #include "../grid.h" #include "../taskbar.h" #include "../pager.h" #include "../popup.h" #include "../tray.h" void config_widget (GScanner *scanner, GtkWidget *widget); GdkRectangle config_get_loc ( GScanner *scanner ) { GdkRectangle rect; rect.x = 0; rect.y = 0; rect.width = 1; rect.height = 1; config_parse_sequence(scanner, SEQ_REQ,'(',NULL,NULL,"missing '(' afer loc", SEQ_REQ,G_TOKEN_INT,NULL,&rect.x,"missing x value in loc", SEQ_REQ,',',NULL,NULL,"missing comma afer x value in loc", SEQ_REQ,G_TOKEN_INT,NULL,&rect.y,"missing y value in loc", SEQ_OPT,',',NULL,NULL,NULL, SEQ_CON,G_TOKEN_INT,NULL,&rect.width,"missing w value in loc", SEQ_OPT,',',NULL,NULL,NULL, SEQ_CON,G_TOKEN_INT,NULL,&rect.height,"missing h value in loc", SEQ_REQ,')',NULL,NULL,"missing ')' in loc statement", SEQ_OPT,';',NULL,NULL,NULL, SEQ_END ); return rect; } void config_get_pins ( GScanner *scanner, GtkWidget *widget ) { scanner->max_parse_errors = FALSE; if(!IS_PAGER(widget)) return g_scanner_error(scanner,"this widget has no property 'pins'"); if(!config_expect_token(scanner, '=',"expecting pins = string [,string]")) return; do { g_scanner_get_next_token(scanner); if(!config_expect_token(scanner, G_TOKEN_STRING, "expecting a string in pins = string [,string]")) break; g_scanner_get_next_token(scanner); pager_add_pin(widget,g_strdup(scanner->value.v_string)); } while ( g_scanner_peek_next_token(scanner)==','); config_optional_semicolon(scanner); } void config_widget_action_index ( GScanner *scanner, gint *button, gint *mod ) { *mod = 0; g_scanner_get_next_token(scanner); while(g_scanner_peek_next_token(scanner)=='+') { switch((gint)(scanner->token)) { case G_TOKEN_SHIFT: *mod |= GDK_SHIFT_MASK; break; case G_TOKEN_CTRL: *mod |= GDK_CONTROL_MASK; break; case G_TOKEN_MOD1: *mod |= GDK_MOD1_MASK; break; case G_TOKEN_MOD2: *mod |= GDK_MOD2_MASK; break; case G_TOKEN_MOD3: *mod |= GDK_MOD3_MASK; break; case G_TOKEN_MOD4: *mod |= GDK_MOD4_MASK; break; case G_TOKEN_MOD5: *mod |= GDK_MOD5_MASK; break; case G_TOKEN_SUPER: *mod |= GDK_SUPER_MASK; break; case G_TOKEN_HYPER: *mod |= GDK_HYPER_MASK; break; case G_TOKEN_META: *mod |= GDK_META_MASK; break; default: g_scanner_error(scanner, "Invalid action key modifier"); } g_scanner_get_next_token(scanner); // get '+' g_scanner_get_next_token(scanner); } switch((gint)(scanner->token)) { case G_TOKEN_FLOAT: *button = scanner->value.v_float; break; case G_TOKEN_INIT: *button = 0; break; case G_TOKEN_LEFT: *button = 1; break; case G_TOKEN_MIDDLE: *button = 2; break; case G_TOKEN_RIGHT: *button = 3; break; case G_TOKEN_SCROLL_UP: *button = 4; break; case G_TOKEN_SCROLL_DOWN: *button = 5; break; case G_TOKEN_SCROLL_LEFT: *button = 6; break; case G_TOKEN_SCROLL_RIGHT: *button = 7; break; default: g_scanner_error(scanner,"invalid action index"); break; } } void config_widget_action ( GScanner *scanner, GtkWidget *widget ) { gint button = 1, mod = 0; if(g_scanner_peek_next_token(scanner)=='[') { g_scanner_get_next_token(scanner); config_widget_action_index(scanner,&button,&mod); if(config_expect_token(scanner, ']', "missing ']' after action")) g_scanner_get_next_token(scanner); } if(config_expect_token(scanner, '=', "missing '=' after action")) g_scanner_get_next_token(scanner); if(scanner->max_parse_errors) return; if( button<0 || button >=8 ) return g_scanner_error(scanner,"invalid action index %d",button); base_widget_set_action(widget,button,mod,config_action(scanner)); if(!base_widget_get_action(widget,button,mod)) return g_scanner_error(scanner,"invalid action"); config_optional_semicolon(scanner); } GtkWidget *config_include ( GScanner *scanner, gboolean toplevel ) { GtkWidget *widget; gchar *fname = NULL; config_parse_sequence(scanner, SEQ_REQ,'(',NULL,NULL,"Missing '(' after include", SEQ_REQ,G_TOKEN_STRING,NULL,&fname,"Missing filename in include", SEQ_REQ,')',NULL,NULL,"Missing ')',after include", SEQ_OPT,';',NULL,NULL,NULL, SEQ_END); if(!scanner->max_parse_errors) widget = config_parse(fname, toplevel); else widget = NULL; g_free(fname); return widget; } gboolean config_widget_property ( GScanner *scanner, GtkWidget *widget ) { GtkWindow *win; if(IS_BASE_WIDGET(widget)) switch ( (gint)scanner->token ) { case G_TOKEN_STYLE: base_widget_set_style(widget, config_get_value(scanner,"style",TRUE,NULL)); return TRUE; case G_TOKEN_CSS: base_widget_set_css(widget, config_assign_string(scanner,"css")); return TRUE; case G_TOKEN_INTERVAL: base_widget_set_interval(widget, 1000*config_assign_number(scanner, "interval")); return TRUE; case G_TOKEN_TRIGGER: base_widget_set_interval(widget,0); base_widget_set_trigger(widget, config_assign_string(scanner, "trigger")); return TRUE; case G_TOKEN_LOC: base_widget_set_rect(widget,config_get_loc(scanner)); return TRUE; case G_TOKEN_ACTION: config_widget_action(scanner, widget); return TRUE; } if(IS_BASE_WIDGET(widget) && !IS_FLOW_GRID(base_widget_get_child(widget))) switch ( (gint)scanner->token ) { case G_TOKEN_VALUE: base_widget_set_value(widget, config_get_value(scanner,"value",TRUE,NULL)); return TRUE; case G_TOKEN_TOOLTIP: base_widget_set_tooltip(widget, config_get_value(scanner,"tooltip",TRUE,NULL)); return TRUE; } if(IS_PAGER(widget)) switch ( (gint)scanner->token ) { case G_TOKEN_PINS: config_get_pins( scanner, widget ); return TRUE; case G_TOKEN_PREVIEW: g_object_set_data(G_OBJECT(base_widget_get_child(widget)),"preview", GINT_TO_POINTER(config_assign_boolean(scanner,FALSE,"preview"))); return TRUE; case G_TOKEN_NUMERIC: g_object_set_data(G_OBJECT(base_widget_get_child(widget)), "sort_numeric",GINT_TO_POINTER( config_assign_boolean(scanner,TRUE,"numeric"))); return TRUE; } if(IS_TASKBAR(widget)) switch ( (gint)scanner->token ) { case G_TOKEN_PEROUTPUT: if(config_assign_boolean(scanner,FALSE,"filter_output")) taskbar_set_filter(widget,G_TOKEN_OUTPUT); g_message("'filter_output' is deprecated, please use 'filter = output' instead"); return TRUE; case G_TOKEN_FILTER: taskbar_set_filter(widget,config_assign_tokens(scanner,"filter", "output|workspace", G_TOKEN_OUTPUT,G_TOKEN_WORKSPACE,G_TOKEN_FLOATING,0)); return TRUE; case G_TOKEN_TITLEWIDTH: g_object_set_data(G_OBJECT(widget),"title_width", GINT_TO_POINTER(config_assign_number(scanner,"title_width"))); return TRUE; case G_TOKEN_GROUP: if(g_scanner_peek_next_token(scanner) == '=') { g_object_set_data(G_OBJECT(widget),"group",GINT_TO_POINTER( config_assign_boolean(scanner,FALSE,"group"))); return TRUE; } switch((gint)g_scanner_get_next_token(scanner)) { case G_TOKEN_COLS: g_object_set_data(G_OBJECT(widget),"g_cols",GINT_TO_POINTER( (gint)config_assign_number(scanner,"group cols"))); return TRUE; case G_TOKEN_ROWS: g_object_set_data(G_OBJECT(widget),"g_rows",GINT_TO_POINTER( (gint)config_assign_number(scanner,"group rows"))); return TRUE; case G_TOKEN_ICONS: g_object_set_data(G_OBJECT(widget),"g_icons", GINT_TO_POINTER(config_assign_boolean(scanner,FALSE,"group icons"))); return TRUE; case G_TOKEN_LABELS: g_object_set_data(G_OBJECT(widget),"g_labels", GINT_TO_POINTER(config_assign_boolean(scanner,FALSE,"group labels"))); return TRUE; case G_TOKEN_CSS: g_object_set_data_full(G_OBJECT(widget),"g_css", config_assign_string(scanner,"group css"),g_free); return TRUE; case G_TOKEN_STYLE: g_object_set_data_full(G_OBJECT(widget),"g_style", config_assign_string(scanner,"group style"),g_free); return TRUE; case G_TOKEN_TITLEWIDTH: g_object_set_data(G_OBJECT(widget),"g_title_width",GINT_TO_POINTER( (gint)config_assign_number(scanner,"group title_width"))); return TRUE; case G_TOKEN_SORT: g_object_set_data(G_OBJECT(widget),"g_sort",GINT_TO_POINTER( config_assign_boolean(scanner,TRUE,"group sort"))); return TRUE; } } if(IS_FLOW_GRID(base_widget_get_child(widget))) switch ( (gint)scanner->token ) { case G_TOKEN_COLS: flow_grid_set_cols(base_widget_get_child(widget), config_assign_number(scanner, "cols")); return TRUE; case G_TOKEN_ROWS: flow_grid_set_rows(base_widget_get_child(widget), config_assign_number(scanner, "rows")); return TRUE; case G_TOKEN_PRIMARY: flow_grid_set_primary(base_widget_get_child(widget), config_assign_tokens(scanner,"primary","rows|cols", G_TOKEN_ROWS,G_TOKEN_COLS,NULL)); return TRUE; case G_TOKEN_ICONS: g_object_set_data(G_OBJECT(widget),"icons", GINT_TO_POINTER(config_assign_boolean(scanner,FALSE,"icons"))); return TRUE; case G_TOKEN_LABELS: g_object_set_data(G_OBJECT(widget),"labels", GINT_TO_POINTER(config_assign_boolean(scanner,FALSE,"labels"))); return TRUE; case G_TOKEN_SORT: flow_grid_set_sort(base_widget_get_child(widget), config_assign_boolean(scanner,TRUE,"sort")); return TRUE; } win = GTK_WINDOW(gtk_widget_get_ancestor(widget,GTK_TYPE_WINDOW)); if(win && gtk_bin_get_child(GTK_BIN(win)) == widget && gtk_window_get_window_type(win) == GTK_WINDOW_POPUP) switch ( (gint)scanner->token ) { case G_TOKEN_AUTOCLOSE: popup_set_autoclose(GTK_WIDGET(win), config_assign_boolean(scanner, FALSE, "autoclose")); return TRUE; } return FALSE; } GtkWidget *config_widget_new ( gint type, GScanner *scanner ) { switch(type) { case G_TOKEN_GRID: return grid_new(); case G_TOKEN_LABEL: return label_new(); case G_TOKEN_IMAGE: return image_new(); case G_TOKEN_BUTTON: return button_new(); case G_TOKEN_SCALE: return scale_new(); case G_TOKEN_CHART: return cchart_new(); case G_TOKEN_INCLUDE: return config_include( scanner, FALSE ); case G_TOKEN_TASKBAR: return taskbar_new(TRUE); case G_TOKEN_PAGER: return pager_new(); case G_TOKEN_TRAY: return tray_new(); } return NULL; } gboolean config_widget_child ( GScanner *scanner, GtkWidget *container ) { GtkWidget *widget, *parent; gint type; if(!IS_GRID(container)) return FALSE; type = scanner->token; switch (type) { case G_TOKEN_GRID: case G_TOKEN_LABEL: case G_TOKEN_IMAGE: case G_TOKEN_BUTTON: case G_TOKEN_SCALE: case G_TOKEN_CHART: case G_TOKEN_INCLUDE: case G_TOKEN_TASKBAR: case G_TOKEN_PAGER: case G_TOKEN_TRAY: break; default: return FALSE; } scanner->max_parse_errors = FALSE; if(g_scanner_peek_next_token(scanner)==G_TOKEN_STRING) { g_scanner_get_next_token(scanner); widget = base_widget_from_id(scanner->value.v_string); parent = widget?gtk_widget_get_parent(widget):NULL; parent = parent?gtk_widget_get_parent(parent):NULL; if(!widget || parent != container) { widget = config_widget_new(type, scanner); base_widget_set_id(widget, g_strdup(scanner->value.v_string)); } } else widget = config_widget_new(type, scanner); config_widget(scanner, widget); if(!gtk_widget_get_parent(widget)) grid_attach(container, widget); css_widget_cascade(widget, NULL); return TRUE; } void config_widget ( GScanner *scanner, GtkWidget *widget ) { if(g_scanner_peek_next_token(scanner) == '{') { g_scanner_get_next_token(scanner); while ( (gint)g_scanner_get_next_token ( scanner ) != '}' && (gint)scanner->token != G_TOKEN_EOF ) { if(!config_widget_property(scanner,widget) && !config_widget_child(scanner,widget)) g_scanner_error(scanner,"Invalid property in a widget declaration"); } if(scanner->token != '}') g_scanner_error(scanner,"Missing '}' at the end of widget properties"); } config_optional_semicolon(scanner); } void config_layout ( GScanner *scanner, GtkWidget **widget, gboolean toplevel ) { GtkWidget *layout; scanner->max_parse_errors=FALSE; if(!toplevel) { if(!*widget) { *widget = grid_new(); gtk_widget_set_name(*widget,"layout"); } layout = *widget; } else { if(g_scanner_peek_next_token(scanner)==G_TOKEN_STRING) { g_scanner_get_next_token(scanner); layout = bar_grid_from_name(scanner->value.v_string); } else layout = bar_grid_from_name(NULL); } config_widget(scanner, layout); } void config_popup ( GScanner *scanner ) { GtkWidget *win, *grid; if(g_scanner_peek_next_token(scanner)!=G_TOKEN_STRING) { g_scanner_error(scanner,"missing identifier after 'popup'"); return; } g_scanner_get_next_token(scanner); win = popup_new(scanner->value.v_string); grid = gtk_bin_get_child(GTK_BIN(win)); config_widget(scanner, grid); } sfwbar-1.0~beta13/src/config/placer.c000066400000000000000000000026131450657570000175150ustar00rootroot00000000000000/* This entire file is licensed under GNU General Public License v3.0 * * Copyright 2022- sfwbar maintainers */ #include "../config.h" void config_placer ( GScanner *scanner ) { gint wp_x= 10; gint wp_y= 10; gint wo_x= 0; gint wo_y= 0; gboolean pid = FALSE; scanner->max_parse_errors = FALSE; if(!config_expect_token(scanner,'{',"Missing '{' after 'placer'")) return; g_scanner_get_next_token(scanner); while (( (gint)g_scanner_peek_next_token ( scanner ) != '}' )&& ( (gint)g_scanner_peek_next_token ( scanner ) != G_TOKEN_EOF )) { switch ((gint)g_scanner_get_next_token(scanner) ) { case G_TOKEN_XSTEP: wp_x = config_assign_number ( scanner, "xstep" ); break; case G_TOKEN_YSTEP: wp_y = config_assign_number ( scanner, "ystep" ); break; case G_TOKEN_XORIGIN: wo_x = config_assign_number ( scanner, "xorigin" ); break; case G_TOKEN_YORIGIN: wo_y = config_assign_number ( scanner, "yorigin" ); break; case G_TOKEN_CHILDREN: pid = config_assign_boolean(scanner,FALSE,"children"); break; default: g_scanner_error(scanner,"Unexpected token in 'placer'"); break; } } if((gint)scanner->next_token == '}') g_scanner_get_next_token(scanner); config_optional_semicolon(scanner); wintree_placer_conf(wp_x,wp_y,wo_x,wo_y,pid); } sfwbar-1.0~beta13/src/config/scanner.c000066400000000000000000000131661450657570000177050ustar00rootroot00000000000000/* This entire file is licensed under GNU General Public License v3.0 * * Copyright 2022- sfwbar maintainers */ #include #include "../config.h" #include "../scanner.h" #include "../sway_ipc.h" #include "../sfwbar.h" #include "../client.h" static gboolean config_var_flag ( GScanner *scanner, gint *flag ) { if(((gint)g_scanner_peek_next_token(scanner) >= G_TOKEN_SUM) && ((gint)(scanner->next_token) <= G_TOKEN_FIRST)) *flag = g_scanner_get_next_token(scanner); return TRUE; } static gboolean config_var_type (GScanner *scanner, gint *type ) { gint token = g_scanner_get_next_token(scanner); if(token == G_TOKEN_REGEX || token == G_TOKEN_JSON || token == G_TOKEN_GRAB) *type = token; else g_scanner_error(scanner,"invalid parser"); return !scanner->max_parse_errors; } static void config_var ( GScanner *scanner, ScanFile *file ) { gchar *vname = NULL, *pattern = NULL; guint type; gint flag = G_TOKEN_LASTW; config_parse_sequence(scanner, SEQ_REQ,G_TOKEN_IDENTIFIER,NULL,&vname,NULL, SEQ_REQ,'=',NULL,NULL,"Missing '=' in variable declaration", SEQ_REQ,-2,(parse_func)config_var_type,&type,NULL, SEQ_REQ,'(',NULL,NULL,"Missing '(' after parser", SEQ_END); if(scanner->max_parse_errors) return g_free(vname); switch(type) { case G_TOKEN_REGEX: case G_TOKEN_JSON: config_parse_sequence(scanner, SEQ_REQ,G_TOKEN_STRING,NULL,&pattern,"Missing pattern in parser", SEQ_OPT,',',NULL,NULL,NULL, SEQ_CON,-2,(parse_func)config_var_flag,&flag,NULL, SEQ_REQ,')',NULL,NULL,"Missing ')' after parser", SEQ_OPT,';',NULL,NULL,NULL, SEQ_END); break; case G_TOKEN_GRAB: config_parse_sequence(scanner, SEQ_OPT,-2,(parse_func)config_var_flag,&flag,NULL, SEQ_REQ,')',NULL,NULL,"Missing ')' after parser", SEQ_OPT,';',NULL,NULL,NULL, SEQ_END); break; default: g_scanner_error(scanner,"invalid parser for variable %s",vname); } if(scanner->max_parse_errors) { g_free(vname); g_free(pattern); return; } scanner_var_new(vname,file,pattern,type,flag); g_free(vname); g_free(pattern); } static gboolean config_source_flags ( GScanner *scanner, gint *flags ) { while ( g_scanner_peek_next_token(scanner) == ',' ) { g_scanner_get_next_token(scanner); g_scanner_get_next_token(scanner); if((gint)scanner->token == G_TOKEN_NOGLOB) *flags |= VF_NOGLOB; else if((gint)scanner->token == G_TOKEN_CHTIME) *flags |= VF_CHTIME; else g_scanner_error(scanner, "invalid flag in source"); } return !scanner->max_parse_errors; } static ScanFile *config_source ( GScanner *scanner, gint source ) { ScanFile *file; gchar *fname = NULL, *trigger = NULL; gint flags = 0; switch(source) { case SO_FILE: config_parse_sequence(scanner, SEQ_REQ,'(',NULL,NULL,"Missing '(' after source", SEQ_REQ,G_TOKEN_STRING,NULL,&fname,"Missing file in a source", SEQ_OPT,-2,(parse_func)config_source_flags,&flags,NULL, SEQ_REQ,')',NULL,NULL,"Missing ')' after source", SEQ_REQ,'{',NULL,NULL,"Missing '{' after source", SEQ_END); break; case SO_CLIENT: config_parse_sequence(scanner, SEQ_REQ,'(',NULL,NULL,"Missing '(' after source", SEQ_REQ,G_TOKEN_STRING,NULL,&fname,"Missing file in a source", SEQ_OPT,',',NULL,NULL,NULL, SEQ_CON,G_TOKEN_STRING,NULL,&trigger,NULL, SEQ_REQ,')',NULL,NULL,"Missing ')' after source", SEQ_REQ,'{',NULL,NULL,"Missing '{' after source", SEQ_END); break; default: config_parse_sequence(scanner, SEQ_REQ,'(',NULL,NULL,"Missing '(' after source", SEQ_REQ,G_TOKEN_STRING,NULL,&fname,"Missing file in a source", SEQ_REQ,')',NULL,NULL,"Missing ')' after source", SEQ_REQ,'{',NULL,NULL,"Missing '{' after source", SEQ_END); break; } if(scanner->max_parse_errors) { g_free(fname); g_free(trigger); return NULL; } file = scanner_file_new ( source, fname, trigger, flags ); while(g_scanner_peek_next_token(scanner) == G_TOKEN_IDENTIFIER) config_var(scanner, file); config_parse_sequence(scanner, SEQ_REQ,'}',NULL,NULL,"Expecting a variable declaration or '}'", SEQ_OPT,';',NULL,NULL,NULL, SEQ_END); return file; } void config_scanner ( GScanner *scanner ) { ScanFile *file; scanner->max_parse_errors = FALSE; if(!config_expect_token(scanner, '{', "Missing '{' after 'scanner'")) return; g_scanner_get_next_token(scanner); while(((gint)g_scanner_peek_next_token(scanner) != '}' )&& ( (gint)g_scanner_peek_next_token ( scanner ) != G_TOKEN_EOF )) { switch((gint)g_scanner_get_next_token(scanner)) { case G_TOKEN_FILE: config_source(scanner,SO_FILE); break; case G_TOKEN_EXEC: config_source(scanner,SO_EXEC); break; case G_TOKEN_MPDCLIENT: file = config_source(scanner,SO_CLIENT); client_mpd(file); break; case G_TOKEN_SWAYCLIENT: file = config_source(scanner,SO_CLIENT); sway_ipc_client_init(file); break; case G_TOKEN_EXECCLIENT: file = config_source(scanner,SO_CLIENT); client_exec(file); break; case G_TOKEN_SOCKETCLIENT: file = config_source(scanner,SO_CLIENT); client_socket(file); break; default: g_scanner_error(scanner, "Unexpected declaration in scanner"); break; } } if((gint)scanner->next_token == '}') g_scanner_get_next_token(scanner); } sfwbar-1.0~beta13/src/config/switcher.c000066400000000000000000000041231450657570000200750ustar00rootroot00000000000000/* This entire file is licensed under GNU General Public License v3.0 * * Copyright 2022- sfwbar maintainers */ #include "../config.h" #include "../switcher.h" void config_switcher ( GScanner *scanner ) { GtkWidget *widget; scanner->max_parse_errors = FALSE; if(!config_expect_token(scanner, '{',"Missing '{' after 'switcher'")) return; g_scanner_get_next_token(scanner); widget = switcher_new(); while (( (gint)g_scanner_peek_next_token ( scanner ) != '}' )&& ( (gint)g_scanner_peek_next_token ( scanner ) != G_TOKEN_EOF )) { switch ((gint)g_scanner_get_next_token ( scanner ) ) { case G_TOKEN_INTERVAL: g_object_set_data(G_OBJECT(widget),"interval", GINT_TO_POINTER(config_assign_number(scanner,"interval")/100)); break; case G_TOKEN_COLS: flow_grid_set_cols(base_widget_get_child(widget), config_assign_number(scanner, "cols")); break; case G_TOKEN_ROWS: flow_grid_set_rows(base_widget_get_child(widget), config_assign_number(scanner, "rows")); break; case G_TOKEN_CSS: css_widget_apply(widget,config_assign_string(scanner,"css")); break; case G_TOKEN_ICONS: g_object_set_data(G_OBJECT(widget),"icons", GINT_TO_POINTER(config_assign_boolean(scanner,FALSE,"icons"))); break; case G_TOKEN_LABELS: g_object_set_data(G_OBJECT(widget),"labels", GINT_TO_POINTER(config_assign_boolean(scanner,FALSE,"labels"))); break; case G_TOKEN_FILTER: switcher_set_filter(widget,config_assign_tokens(scanner,"filter", "output|workspace", G_TOKEN_OUTPUT,G_TOKEN_WORKSPACE,0)); break; case G_TOKEN_TITLEWIDTH: g_object_set_data(G_OBJECT(widget),"title_width", GINT_TO_POINTER(config_assign_number(scanner,"title_width"))); break; default: g_scanner_error(scanner,"Unexpected token in 'switcher'"); break; } } if((gint)scanner->next_token == '}') g_scanner_get_next_token(scanner); config_optional_semicolon(scanner); } sfwbar-1.0~beta13/src/config/toplevel.c000066400000000000000000000364241450657570000201100ustar00rootroot00000000000000/* This entire file is licensed under GNU General Public License v3.0 * * Copyright 2022- sfwbar maintainers */ #include "../config.h" #include "../menu.h" #include "../sfwbar.h" #include "../module.h" #include "../bar.h" static GHashTable *defines; gchar *config_value_string ( gchar *dest, gchar *string ) { gint i,j=0,l; gchar *result; l = strlen(dest); for(i=0;string[i];i++) if(string[i] == '"' || string[i] == '\\') j++; result = g_malloc(l+i+j+3); memcpy(result,dest,l); result[l++]='"'; for(i=0;string[i];i++) { if(string[i] == '"' || string[i] == '\\') result[l++]='\\'; result[l++] = string[i]; } result[l++]='"'; result[l]=0; g_free(dest); return result; } gchar *config_get_value ( GScanner *scanner, gchar *prop, gboolean assign, gchar **id ) { gchar *value, *temp; gint pcount = 0; static gchar buf[G_ASCII_DTOSTR_BUF_SIZE]; scanner->max_parse_errors = FALSE; if(assign) { if(!config_expect_token(scanner, '=',"expecting %s = expression",prop)) return NULL; g_scanner_get_next_token(scanner); } if(id && g_scanner_peek_next_token(scanner)==G_TOKEN_STRING) { g_scanner_get_next_token(scanner); temp = g_strdup(scanner->value.v_string); if(g_scanner_peek_next_token(scanner)==',') { g_scanner_get_next_token(scanner); value = g_strdup("");; *id = temp; } else { value = config_value_string(g_strdup(""),temp); g_free(temp); } } else value = g_strdup("");; g_scanner_peek_next_token(scanner); scanner->token = '+'; while(((gint)scanner->next_tokennext_token!='}')&& (scanner->next_token!=';')&& (scanner->next_token!='[')&& (scanner->next_token!=',' || pcount)&& (scanner->next_token!=')' || pcount)&& (scanner->next_token!=G_TOKEN_IDENTIFIER || strchr(",(+-*/%=<>!|&",scanner->token))&& (scanner->next_token!=G_TOKEN_EOF)) { switch((gint)g_scanner_get_next_token(scanner)) { case G_TOKEN_STRING: value = config_value_string(value, scanner->value.v_string); break; case G_TOKEN_IDENTIFIER: temp = value; if(defines&&g_hash_table_contains(defines,scanner->value.v_identifier)) value = g_strconcat(value, g_hash_table_lookup(defines,scanner->value.v_identifier), NULL); else value = g_strconcat(value, scanner->value.v_identifier, NULL); g_free(temp); break; case G_TOKEN_FLOAT: temp = value; value = g_strconcat(temp,g_ascii_dtostr(buf,G_ASCII_DTOSTR_BUF_SIZE, scanner->value.v_float),NULL); g_free(temp); break; default: temp = value; buf[0] = scanner->token; buf[1] = 0; value = g_strconcat(temp,buf,NULL); g_free(temp); break; } if(scanner->token == '(') pcount++; else if(scanner->token == ')') pcount--; g_scanner_peek_next_token(scanner); } config_optional_semicolon(scanner); return value; } void config_action_conditions ( GScanner *scanner, guchar *cond, guchar *ncond ) { guchar *ptr; if(g_scanner_peek_next_token(scanner) != '[') return; do { g_scanner_get_next_token(scanner); if(g_scanner_peek_next_token(scanner)=='!') { g_scanner_get_next_token(scanner); ptr = ncond; } else ptr = cond; switch((gint)g_scanner_get_next_token(scanner)) { case G_TOKEN_FOCUSED: *ptr |= WS_FOCUSED; break; case G_TOKEN_MINIMIZED: *ptr |= WS_MINIMIZED; break; case G_TOKEN_MAXIMIZED: *ptr |= WS_MAXIMIZED; break; case G_TOKEN_FULLSCREEN: *ptr |= WS_FULLSCREEN; break; case G_TOKEN_USERSTATE: *ptr |= WS_USERSTATE; break; case G_TOKEN_USERSTATE2: *ptr |= WS_USERSTATE2; break; case G_TOKEN_CHILDREN: *ptr |= WS_CHILDREN; break; default: g_scanner_error(scanner,"invalid condition in action"); break; } } while (g_scanner_peek_next_token(scanner)=='|'); if(g_scanner_get_next_token(scanner) != ']') g_scanner_error(scanner,"missing ']' in conditional action"); } action_t *config_action ( GScanner *scanner ) { action_t *action; gchar *lname; action = action_new(); config_action_conditions ( scanner, &action->cond, &action->ncond ); g_scanner_get_next_token(scanner); if(scanner->token == G_TOKEN_STRING) { action->command->cache = g_strdup(scanner->value.v_string); action->quark = g_quark_from_static_string("exec"); } else { switch ((gint)scanner->token) { case G_TOKEN_MENU: action->quark = g_quark_from_static_string("menu"); break; case G_TOKEN_EXEC: action->quark = g_quark_from_static_string("exec"); break; case G_TOKEN_POPUP: action->quark = g_quark_from_static_string("popup"); break; case G_TOKEN_USERSTATE: action->quark = g_quark_from_static_string("userstate"); break; case G_TOKEN_FUNCTION: action->quark = g_quark_from_static_string("function"); break; case G_TOKEN_MENUCLEAR: action->quark = g_quark_from_static_string("menuclear"); break; case G_TOKEN_IDENTIFIER: lname = g_ascii_strdown(scanner->value.v_identifier,-1); action->quark = g_quark_from_string(lname); g_free(lname); break; default: g_scanner_error(scanner,"invalid action"); break; } if(!scanner->max_parse_errors) { config_parse_sequence(scanner, SEQ_OPT,G_TOKEN_VALUE,NULL,&action->addr->definition, "Missing argument in action", SEQ_OPT,',',NULL,NULL,NULL, SEQ_CON,G_TOKEN_VALUE,NULL,&action->command->definition, "Missing argument after ','", SEQ_END); action->addr->eval = TRUE; action->command->eval = TRUE; if(!action->command->definition && action->addr->definition) { action->command->definition = action->addr->definition; action->addr->definition = NULL; action->addr->eval = FALSE; } } } if(scanner->max_parse_errors) { action_free(action,NULL); return NULL; } return action; } GtkWidget *config_menu_item ( GScanner *scanner ) { gchar *label = NULL; action_t *action; GtkWidget *item; config_parse_sequence(scanner, SEQ_REQ,'(',NULL,NULL,"missing '(' after 'item'", SEQ_REQ,G_TOKEN_STRING,NULL,&label,"missing label in 'item'", SEQ_REQ,',',NULL,NULL,"missing ',' in 'item'", SEQ_END); if(scanner->max_parse_errors) { g_free(label); return NULL; } action = config_action(scanner); if(!action) { g_scanner_error(scanner, "menu item: invalid action"); g_free(label); return NULL; } if(g_scanner_get_next_token(scanner)!=')') { g_scanner_error(scanner,"missing ')' after 'item'"); action_free(action,NULL); g_free(label); return NULL; } config_optional_semicolon(scanner); item = menu_item_new(label,action); g_free(label); return item; } void config_menu_items ( GScanner *scanner, GtkWidget *menu ) { GtkWidget *item, *submenu; gchar *itemname, *subname; gboolean items; g_scanner_peek_next_token(scanner); while(scanner->next_token != G_TOKEN_EOF && scanner->next_token != '}') { item = NULL; switch((gint)g_scanner_get_next_token(scanner)) { case G_TOKEN_ITEM: item = config_menu_item(scanner); break; case G_TOKEN_SEPARATOR: item = gtk_separator_menu_item_new(); config_optional_semicolon(scanner); break; case G_TOKEN_SUBMENU: itemname = NULL; subname = NULL; items = FALSE; config_parse_sequence(scanner, SEQ_REQ,'(',NULL,NULL,"missing '(' after 'submenu'", SEQ_REQ,G_TOKEN_STRING,NULL,&itemname,"missing submenu title", SEQ_OPT,',',NULL,NULL,NULL, SEQ_CON,G_TOKEN_STRING,NULL,&subname,"missing submenu name", SEQ_REQ,')',NULL,NULL,"missing ')' afer 'submenu'", SEQ_OPT,'{',NULL,&items,"missing '{' afer 'submenu'", SEQ_END); if(!scanner->max_parse_errors && itemname) { item = menu_item_new(itemname,NULL); submenu = menu_new(subname); gtk_menu_item_set_submenu(GTK_MENU_ITEM(item),submenu); if(items) config_menu_items(scanner,submenu); } g_free(itemname); g_free(subname); break; default: g_scanner_error(scanner, "Unexpected token in menu. Expecting a menu item"); break; } if(item) gtk_container_add(GTK_CONTAINER(menu),item); g_scanner_peek_next_token(scanner); } if(scanner->next_token == '}') g_scanner_get_next_token(scanner); } void config_menu ( GScanner *scanner ) { gchar *name = NULL; GtkWidget *menu; gboolean items; config_parse_sequence(scanner, SEQ_REQ,'(',NULL,NULL,"missing '(' after 'menu'", SEQ_REQ,G_TOKEN_STRING,NULL,&name,"missing menu name", SEQ_REQ,')',NULL,NULL,"missing ')' afer 'menu'", SEQ_REQ,'{',NULL,&items,"missing '{' afer 'menu'", SEQ_END); if(!scanner->max_parse_errors && name) { menu = menu_new(name); if(items) config_menu_items(scanner, menu); } g_free(name); config_optional_semicolon(scanner); } void config_menu_clear ( GScanner *scanner ) { gchar *name = NULL; config_parse_sequence(scanner, SEQ_REQ,'(',NULL,NULL,"missing '(' after 'menu'", SEQ_REQ,G_TOKEN_STRING,NULL,&name,"missing menu name", SEQ_REQ,')',NULL,NULL,"missing ')' afer 'menu'", SEQ_OPT,';',NULL,NULL,NULL, SEQ_END); if(!scanner->max_parse_errors && name) menu_remove(name); g_free(name); } void config_function ( GScanner *scanner ) { gchar *name = NULL; GList *actions = NULL; action_t *action; config_parse_sequence(scanner, SEQ_REQ,'(',NULL,NULL,"missing '(' after 'function'", SEQ_REQ,G_TOKEN_STRING,NULL,&name,"missing function name", SEQ_REQ,')',NULL,NULL,"missing ')' afer 'function'", SEQ_OPT,'{',NULL,NULL,"missing '{' afer 'function'", SEQ_END); if(scanner->max_parse_errors) return g_free(name); g_scanner_peek_next_token(scanner); while(scanner->next_token != G_TOKEN_EOF && scanner->next_token != '}') { action = config_action(scanner); if(!action) g_scanner_error(scanner,"invalid action"); else actions = g_list_append(actions, action); g_scanner_peek_next_token(scanner); } config_parse_sequence(scanner, SEQ_REQ,'}',NULL,NULL,"Expecting an action or '}'", SEQ_OPT,';',NULL,NULL,NULL, SEQ_END); action_function_add(name,actions); } void config_define ( GScanner *scanner ) { gchar *ident; gchar *value; if(!config_expect_token(scanner, G_TOKEN_IDENTIFIER, "Missing identifier after 'define'")) return; g_scanner_get_next_token(scanner); ident = g_strdup(scanner->value.v_identifier); value = config_get_value(scanner,"define",TRUE,NULL); if(!value) { g_free(ident); return; } if(!defines) defines = g_hash_table_new_full((GHashFunc)str_nhash, (GEqualFunc)str_nequal,g_free,g_free); g_hash_table_insert(defines,ident,value); } void config_set ( GScanner *scanner ) { gchar *ident; gchar *value; if(!config_expect_token(scanner, G_TOKEN_IDENTIFIER, "Missing identifier after 'set'")) return; g_scanner_get_next_token(scanner); ident = g_strdup(scanner->value.v_identifier); value = config_get_value(scanner,"set",TRUE,NULL); if(!value) { g_free(ident); return; } scanner_var_new(ident,NULL,value,G_TOKEN_SET,G_TOKEN_FIRST); g_free(ident); g_free(value); } void config_mappid_map ( GScanner *scanner ) { gchar *pattern, *appid; config_parse_sequence(scanner, SEQ_REQ,G_TOKEN_STRING,NULL,&pattern,"missing pattern in MapAppId", SEQ_REQ,',',NULL,NULL,"missing comma after pattern in MapAppId", SEQ_REQ,G_TOKEN_STRING,NULL,&appid,"missing app_id in MapAppId", SEQ_OPT,';',NULL,NULL,NULL, SEQ_END); if(!scanner->max_parse_errors) wintree_appid_map_add(pattern,appid); g_free(pattern); g_free(appid); } void config_trigger_action ( GScanner *scanner ) { gchar *trigger; action_t *action; config_parse_sequence(scanner, SEQ_REQ,G_TOKEN_STRING,NULL,&trigger,"missing trigger in TriggerAction", SEQ_REQ,',',NULL,NULL,"missing ',' in TriggerAction", SEQ_END); if(scanner->max_parse_errors) return g_free(trigger); action = config_action(scanner); if(!action) return g_free(trigger); action_trigger_add(action,trigger); config_optional_semicolon(scanner); } void config_module ( GScanner *scanner ) { gchar *name = NULL; config_parse_sequence(scanner, SEQ_REQ,'(',NULL,NULL,"missing '(' after 'module'", SEQ_REQ,G_TOKEN_STRING,NULL,&name,"missing module name", SEQ_REQ,')',NULL,NULL,"missing ')' afer 'module'", SEQ_OPT,';',NULL,NULL,NULL, SEQ_END); if(scanner->max_parse_errors || !name) return; module_load ( name ); g_free(name); } GtkWidget *config_parse_toplevel ( GScanner *scanner, gboolean toplevel ) { GtkWidget *w=NULL; while(g_scanner_peek_next_token(scanner) != G_TOKEN_EOF) { switch((gint)g_scanner_get_next_token(scanner)) { case G_TOKEN_SCANNER: config_scanner(scanner); break; case G_TOKEN_LAYOUT: config_layout(scanner,&w,toplevel); break; case G_TOKEN_POPUP: config_popup(scanner); break; case G_TOKEN_PLACER: config_placer(scanner); break; case G_TOKEN_SWITCHER: config_switcher(scanner); break; case G_TOKEN_MENU: config_menu(scanner); break; case G_TOKEN_MENUCLEAR: config_menu_clear(scanner); break; case G_TOKEN_INCLUDE: config_include(scanner,TRUE); break; case G_TOKEN_DEFINE: config_define(scanner); break; case G_TOKEN_SET: config_set(scanner); break; case G_TOKEN_TRIGGERACTION: config_trigger_action(scanner); break; case G_TOKEN_THEME: bar_set_theme(config_assign_string(scanner,"theme")); break; case G_TOKEN_MAPAPPID: config_mappid_map(scanner); break; case G_TOKEN_FILTERAPPID: if(!config_expect_token(scanner, G_TOKEN_STRING, "Missing after FilterAppId")) break; g_scanner_get_next_token(scanner); wintree_filter_appid(scanner->value.v_string); break; case G_TOKEN_FILTERTITLE: if(!config_expect_token(scanner, G_TOKEN_STRING, "Missing after FilterTitle")) break; g_scanner_get_next_token(scanner); wintree_filter_title(scanner->value.v_string); break; case G_TOKEN_FUNCTION: config_function(scanner); break; case G_TOKEN_MODULE: config_module(scanner); break; case G_TOKEN_DISOWNMINIMIZED: wintree_set_disown(config_assign_boolean(scanner,FALSE, "DisownMinimized")); break; default: g_scanner_error(scanner,"Unexpected toplevel token"); break; } } return w; } sfwbar-1.0~beta13/src/css.c000066400000000000000000000142221450657570000155710ustar00rootroot00000000000000/* This entire file is licensed under GNU General Public License v3.0 * * Copyright 2022- sfwbar maintainers */ #include "sfwbar.h" #include "basewidget.h" #include "bar.h" static void (*css_style_updated_original)(GtkWidget *); void css_file_load ( gchar *name ) { GtkCssProvider *css; gchar *fname; if(!name) return; fname = get_xdg_config_file(name,NULL); if(!fname) return; css = gtk_css_provider_new(); gtk_css_provider_load_from_path(css,fname,NULL); gtk_style_context_add_provider_for_screen(gdk_screen_get_default(), GTK_STYLE_PROVIDER(css),GTK_STYLE_PROVIDER_PRIORITY_USER); g_object_unref(css); g_free(fname); } static void css_custom_handle ( GtkWidget *widget ) { gboolean state; gdouble xalign; GtkAlign align; guint x; gtk_widget_style_get(widget,"visible",&state,NULL); if(state) gtk_widget_show_now(widget); else gtk_widget_hide(widget); if(!GTK_IS_EVENT_BOX(widget)) { gtk_widget_style_get(widget,"hexpand",&state,NULL); gtk_widget_set_hexpand(widget,state); gtk_widget_style_get(widget,"vexpand",&state,NULL); gtk_widget_set_vexpand(widget,state); gtk_widget_style_get(widget,"halign",&align,NULL); gtk_widget_set_halign(widget,align); gtk_widget_style_get(widget,"valign",&align,NULL); gtk_widget_set_valign(widget,align); } if(IS_BASE_WIDGET(widget)) { gtk_widget_style_get(gtk_bin_get_child(GTK_BIN(widget)),"max-width",&x, NULL); base_widget_set_max_width(widget,x); gtk_widget_style_get(gtk_bin_get_child(GTK_BIN(widget)),"max-height",&x, NULL); base_widget_set_max_height(widget,x); } if(GTK_IS_LABEL(widget)) { gtk_widget_style_get(widget,"align",&xalign,NULL); gtk_label_set_xalign(GTK_LABEL(widget),xalign); gtk_widget_style_get(widget,"ellipsize",&state,NULL); gtk_label_set_ellipsize(GTK_LABEL(widget), state?PANGO_ELLIPSIZE_END:PANGO_ELLIPSIZE_NONE); } bar_handle_direction(widget); } static void css_style_updated ( GtkWidget *widget ) { css_style_updated_original(widget); css_custom_handle(widget); } void css_init ( gchar *cssname ) { gchar *css_str; GtkCssProvider *css; GtkWidgetClass *widget_class = g_type_class_ref(GTK_TYPE_WIDGET); gtk_widget_class_install_style_property( widget_class, g_param_spec_double("align","text alignment","text alignment", 0.0,1.0,0.5, G_PARAM_READABLE)); gtk_widget_class_install_style_property( widget_class, g_param_spec_boolean("ellipsize","ellipsize text","ellipsize text", TRUE, G_PARAM_READABLE)); gtk_widget_class_install_style_property( widget_class, g_param_spec_boolean("hexpand","horizonal expansion","horizontal expansion", FALSE, G_PARAM_READABLE)); gtk_widget_class_install_style_property( widget_class, g_param_spec_boolean("vexpand","vertical expansion","vertical expansion", FALSE, G_PARAM_READABLE)); gtk_widget_class_install_style_property( widget_class, g_param_spec_boolean("visible","show/hide a widget","show/hide a widget", TRUE, G_PARAM_READABLE)); gtk_widget_class_install_style_property( widget_class, g_param_spec_int("icon-size","icon size","icon size", 0,500,48, G_PARAM_READABLE)); gtk_widget_class_install_style_property( widget_class, g_param_spec_uint("max-width","maximum width","maximum width", 0,G_MAXUINT,0, G_PARAM_READABLE)); gtk_widget_class_install_style_property( widget_class, g_param_spec_uint("max-height","maximum height","maximum height", 0,G_MAXUINT,0, G_PARAM_READABLE)); static GEnumValue dir_types [] = { {GTK_POS_TOP,"top","top"}, {GTK_POS_BOTTOM,"bottom","bottom"}, {GTK_POS_LEFT,"left","left"}, {GTK_POS_RIGHT,"right","right"}, {0,NULL,NULL}}; gtk_widget_class_install_style_property( widget_class, g_param_spec_enum("direction","direction","direction", g_enum_register_static ("direction",dir_types), GTK_POS_RIGHT, G_PARAM_READABLE)); static GEnumValue align_types [] = { {GTK_ALIGN_FILL,"fill","fill"}, {GTK_ALIGN_START,"start","start"}, {GTK_ALIGN_END,"end","end"}, {GTK_ALIGN_CENTER,"center","center"}, {GTK_ALIGN_BASELINE,"baseline","baseline"}}; gtk_widget_class_install_style_property( widget_class, g_param_spec_enum("halign","horizontal alignment","horizontal alignment", g_enum_register_static ("halign",align_types), GTK_ALIGN_FILL, G_PARAM_READABLE)); gtk_widget_class_install_style_property( widget_class, g_param_spec_enum("valign","vertical alignment","vertical alignment", g_enum_register_static ("valign",align_types), GTK_ALIGN_FILL, G_PARAM_READABLE)); css_style_updated_original = widget_class->style_updated; widget_class->style_updated = css_style_updated; css_str = "window { -GtkWidget-direction: bottom; } " \ ".sensor { min-width: 1px; min-height: 1px; background: none; }" \ ".hidden { -GtkWidget-visible: false; }"; css = gtk_css_provider_new(); gtk_css_provider_load_from_data(css,css_str,strlen(css_str),NULL); gtk_style_context_add_provider_for_screen(gdk_screen_get_default(), GTK_STYLE_PROVIDER(css),GTK_STYLE_PROVIDER_PRIORITY_USER); g_object_unref(css); css_file_load(cssname); } void css_widget_apply ( GtkWidget *widget, gchar *css ) { GtkStyleContext *cont; GtkCssProvider *provider; if(!css) return; cont = gtk_widget_get_style_context (widget); provider = gtk_css_provider_new(); gtk_css_provider_load_from_data(provider,css,strlen(css),NULL); gtk_style_context_add_provider (cont, GTK_STYLE_PROVIDER(provider), GTK_STYLE_PROVIDER_PRIORITY_USER); g_object_unref(provider); g_free(css); } void css_widget_cascade ( GtkWidget *widget, gpointer data ) { css_custom_handle(widget); if(GTK_IS_CONTAINER(widget)) gtk_container_forall(GTK_CONTAINER(widget),css_widget_cascade,NULL); } void css_add_class ( GtkWidget *widget, gchar *css_class ) { GtkStyleContext *context; context = gtk_widget_get_style_context(widget); gtk_style_context_add_class(context,css_class); } void css_remove_class ( GtkWidget *widget, gchar *css_class ) { GtkStyleContext *context; context = gtk_widget_get_style_context(widget); gtk_style_context_remove_class(context,css_class); } sfwbar-1.0~beta13/src/expr.c000066400000000000000000000523011450657570000157570ustar00rootroot00000000000000/* This entire file is licensed under GNU General Public License v3.0 * * Copyright 2020- sfwbar maintainers */ #include #include #include #include #include #include "expr.h" #include "sfwbar.h" #include "wintree.h" #include "module.h" static GHashTable *expr_deps; static gdouble expr_parse_num ( GScanner *scanner, gdouble * ); static gchar *expr_parse_str ( GScanner *scanner, gchar * ); static gchar *expr_parse_root ( GScanner *scanner ); static gdouble expr_str_to_num ( gchar *str ) { gdouble val; if(!str) return 0; val = strtod(str,NULL); g_free(str); return val; } void expr_print_msg ( GScanner *scanner, gchar *msg, gboolean error ) { g_message("%s:%d:%d: %s: %s",scanner->input_name,scanner->line, scanner->position,error?"error":"warning",msg); } /* convert a number to a string with specified number of decimals */ gchar *expr_dtostr ( double num, gint dec ) { static const gchar *format = "%%0.%df"; static gchar fbuf[16]; static gchar buf[G_ASCII_DTOSTR_BUF_SIZE]; if(dec<0) return g_strdup(g_ascii_dtostr(buf,G_ASCII_DTOSTR_BUF_SIZE,num)); if(dec>99) dec = 99; g_snprintf(fbuf,16,format,dec); return g_strdup(g_ascii_formatd(buf,G_ASCII_DTOSTR_BUF_SIZE,fbuf,num)); } static gboolean expr_is_numeric ( GScanner *scanner ) { g_scanner_peek_next_token(scanner); if (scanner->next_token == G_TOKEN_FLOAT || scanner->next_token == (gint)G_TOKEN_IDENT || scanner->next_token == '!' || scanner->next_token == '(' || scanner->next_token == '-' || scanner->next_token == (GTokenType)G_TOKEN_LEFT_PAREN) return TRUE; if(scanner->next_token != G_TOKEN_IDENTIFIER) return FALSE; if(scanner_is_variable(scanner->next_value.v_identifier)) return (*(scanner->next_value.v_identifier) != '$'); if(module_is_function(scanner->next_value.v_identifier)) return module_check_flag(scanner->next_value.v_identifier, MODULE_EXPR_NUMERIC); return FALSE; } static gboolean expr_is_string ( GScanner *scanner ) { g_scanner_peek_next_token(scanner); if(scanner->next_token == G_TOKEN_STRING || scanner->next_token == (GTokenType)G_TOKEN_MAP || scanner->next_token == (GTokenType)G_TOKEN_LOOKUP ) return TRUE; if(scanner->next_token != G_TOKEN_IDENTIFIER) return FALSE; if(scanner_is_variable(scanner->next_value.v_identifier)) return (*(scanner->next_value.v_identifier) == '$'); if(module_is_function(scanner->next_value.v_identifier)) return !module_check_flag(scanner->next_value.v_identifier, MODULE_EXPR_NUMERIC); return FALSE; } static gboolean expr_is_variant ( GScanner *scanner ) { g_scanner_peek_next_token(scanner); if( scanner->next_token == (GTokenType)G_TOKEN_IF || scanner->next_token == (GTokenType)G_TOKEN_CACHED) return TRUE; if( scanner->next_token != G_TOKEN_IDENTIFIER || scanner_is_variable(scanner->next_value.v_identifier) || module_is_function(scanner->next_value.v_identifier) ) return FALSE; else return TRUE; } static gboolean parser_expect_symbol ( GScanner *scanner, gint symbol, gchar *expr ) { if(g_scanner_get_next_token(scanner)==symbol) return TRUE; g_scanner_unexp_token(scanner, symbol, NULL, NULL, "", expr, TRUE); return FALSE; } static void *expr_parse_identifier ( GScanner *scanner ) { gint i; gchar *err; if(g_scanner_peek_next_token(scanner)!='(' && scanner_is_variable(scanner->value.v_identifier)) return scanner_get_value(scanner->value.v_identifier, !E_STATE(scanner)->ignore,E_STATE(scanner)->expr); if(g_scanner_peek_next_token(scanner)=='(' && module_is_function(scanner->value.v_identifier) && !E_STATE(scanner)->ignore) return module_get_string(scanner); expr_dep_add(scanner->value.v_identifier,E_STATE(scanner)->expr); if(g_scanner_peek_next_token(scanner)!='(') return g_strdup_printf("Undeclared variable: %s", scanner->value.v_identifier); else { err = g_strdup_printf("Unknown Function: %s",scanner->value.v_identifier); g_scanner_get_next_token(scanner); i=1; while(i && !g_scanner_eof(scanner)) switch((gint)g_scanner_get_next_token(scanner)) { case '(': i++; break; case ')': i--; break; } return err; } } static gchar *expr_parse_map ( GScanner *scanner ) { gchar *match, *result, *comp; guint istate = E_STATE(scanner)->ignore; parser_expect_symbol(scanner,'(',"Map(...)"); match = expr_parse_str(scanner,NULL); parser_expect_symbol(scanner,',',"Map(value,...)"); result = NULL; while(scanner->token==',' && expr_is_string(scanner)) { comp = expr_parse_str(scanner,NULL); if(g_scanner_peek_next_token(scanner)==')') { if(!result) result = comp; break; } parser_expect_symbol(scanner,',',"Map(... match , string ...)"); if(!g_strcmp0(comp,match)) result = expr_parse_str(scanner,NULL); else { E_STATE(scanner)->ignore = 1; g_free(expr_parse_str(scanner,NULL)); E_STATE(scanner)->ignore = istate; } g_free(comp); if(g_scanner_peek_next_token(scanner)==',') g_scanner_get_next_token(scanner); } g_free(match); parser_expect_symbol(scanner,')',"Map(...)"); return result; } static gchar *expr_parse_lookup ( GScanner *scanner ) { gchar *str; gdouble value, comp; guint istate = E_STATE(scanner)->ignore; parser_expect_symbol(scanner,'(',"Lookup(...)"); value = expr_parse_num ( scanner, NULL ); parser_expect_symbol(scanner,',',"Lookup(value,...)"); str = NULL; while (scanner->token==',' && expr_is_numeric(scanner)) { comp = expr_parse_num(scanner, NULL); parser_expect_symbol(scanner,',',"Lookup(... threshold, value ...)"); if(compignore = 1; g_free(expr_parse_str(scanner,NULL)); E_STATE(scanner)->ignore = istate; } if(g_scanner_peek_next_token(scanner)==',') g_scanner_get_next_token(scanner); } if(scanner->token==',') { if(!str) str = expr_parse_str(scanner,NULL); else { E_STATE(scanner)->ignore = 1; g_free(expr_parse_str(scanner,NULL)); E_STATE(scanner)->ignore = istate; } } parser_expect_symbol(scanner,')',"Lookup(...)"); return str?str:g_strdup(""); } static gchar *expr_parse_cached ( GScanner *scanner ) { gchar *ret; gint istate; parser_expect_symbol(scanner,'(',"Cached(...)"); istate = E_STATE(scanner)->ignore; E_STATE(scanner)->ignore = TRUE; ret = expr_parse_root(scanner); E_STATE(scanner)->ignore = istate; parser_expect_symbol(scanner,')',"Cached(...)"); return ret; } static gchar *expr_parse_if ( GScanner *scanner ) { gboolean condition; gchar *str1, *str2; guint istate; gint rtype, stype; stype = E_STATE(scanner)->type; istate = E_STATE(scanner)->ignore; parser_expect_symbol(scanner,'(',"If(..."); condition = expr_parse_num(scanner,NULL); E_STATE(scanner)->type = stype; if(!condition) E_STATE(scanner)->ignore = TRUE; parser_expect_symbol(scanner,',',"If(Condition,...)"); str1 = expr_parse_root(scanner); rtype = E_STATE(scanner)->type; if(condition) E_STATE(scanner)->ignore = TRUE; else E_STATE(scanner)->ignore = istate; E_STATE(scanner)->type = stype; parser_expect_symbol(scanner,',',"If(Condition,Expression,...)"); str2 = expr_parse_root(scanner); E_STATE(scanner)->ignore = istate; parser_expect_symbol(scanner,')',"If(Condition,Expression,Expression)"); if(condition) { E_STATE(scanner)->type = rtype; g_free(str2); return str1; } else { g_free(str1); return str2; } } gdouble expr_parse_ident ( GScanner *scanner ) { gdouble result; parser_expect_symbol(scanner,'(',"Ident(Identifier)"); if(!parser_expect_symbol(scanner,G_TOKEN_IDENTIFIER,"Ident(Identifier)")) return FALSE; result = scanner_is_variable(scanner->value.v_identifier) || module_is_function(scanner->value.v_identifier); if(!result) expr_dep_add(scanner->value.v_identifier,E_STATE(scanner)->expr); parser_expect_symbol(scanner,')',"Ident(iIdentifier)"); return result; } /* compare operator for strigs and variants */ static gdouble expr_parse_compare ( GScanner *scanner, gchar *prev ) { gchar *str1, *str2; gboolean negate = FALSE; gdouble res; if(!prev) str1 = expr_parse_str(scanner,NULL); else str1 = prev; if(g_scanner_peek_next_token(scanner)=='!') { negate = TRUE; g_scanner_get_next_token(scanner); } parser_expect_symbol(scanner,'=',"string = string"); str2 = expr_parse_str(scanner,NULL); res = !g_strcmp0(str1,str2); if(negate) res = !res; g_free(str1); g_free(str2); E_STATE(scanner)->type = EXPR_NUMERIC; return res; } static gchar *expr_parse_variant_token ( GScanner *scanner ) { E_STATE(scanner)->type = EXPR_VARIANT; switch((gint)g_scanner_peek_next_token(scanner)) { case G_TOKEN_IF: g_scanner_get_next_token(scanner); return expr_parse_if(scanner); case G_TOKEN_CACHED: g_scanner_get_next_token(scanner); return expr_parse_cached(scanner); case G_TOKEN_IDENTIFIER: g_scanner_get_next_token(scanner); return expr_parse_identifier(scanner); default: return g_strdup(""); } } static gchar *expr_parse_variant ( GScanner *scanner ) { gchar *str; str = expr_parse_variant_token(scanner); while(E_STATE(scanner)->type==EXPR_VARIANT && g_scanner_peek_next_token(scanner) == '+') { g_scanner_get_next_token(scanner); g_free(str); str = expr_parse_root(scanner); } if(E_STATE(scanner)->type == EXPR_STRING) return expr_parse_str(scanner,str); return str; } static gchar *expr_parse_str_l1 ( GScanner *scanner ) { gchar *str; if(expr_is_variant(scanner)) { E_STATE(scanner)->type = EXPR_STRING; str = expr_parse_variant_token(scanner); if(E_STATE(scanner)->type == EXPR_NUMERIC) g_scanner_unexp_token(scanner,G_TOKEN_STRING,NULL,NULL,"","",TRUE); return str; } switch((gint)g_scanner_get_next_token(scanner)) { case G_TOKEN_STRING: return g_strdup(scanner->value.v_string); case G_TOKEN_LOOKUP: return expr_parse_lookup ( scanner ); case G_TOKEN_MAP: return expr_parse_map ( scanner ); case G_TOKEN_IDENTIFIER: return expr_parse_identifier(scanner); default: g_scanner_unexp_token(scanner,G_TOKEN_STRING,NULL,NULL,"","",TRUE); return g_strdup(""); } } static gchar *expr_parse_str ( GScanner *scanner, gchar *prev ) { gchar *str,*next,*tmp; E_STATE(scanner)->type = EXPR_STRING; if(prev) str = prev; else str = expr_parse_str_l1( scanner ); while(g_scanner_peek_next_token( scanner )=='+' && E_STATE(scanner)->type != EXPR_NUMERIC) { g_scanner_get_next_token( scanner ); next = expr_parse_str_l1( scanner ); tmp = g_strconcat(str,next,NULL); g_free(str); g_free(next); str=tmp; } E_STATE(scanner)->type = EXPR_STRING; return str; } static gdouble expr_parse_num_value ( GScanner *scanner, gdouble *prev ) { gdouble val, *ptr; gchar *str; if(prev) return *prev; if(expr_is_string(scanner)) return expr_parse_compare(scanner,NULL); if(expr_is_variant(scanner)) { E_STATE(scanner)->type = EXPR_NUMERIC; str = expr_parse_variant_token(scanner); if(E_STATE(scanner)->type == EXPR_NUMERIC) return expr_str_to_num(str); /* if type is string, assume it's there for comparison */ else if(E_STATE(scanner)->type == EXPR_STRING || g_scanner_peek_next_token(scanner) == '=' || g_scanner_peek_next_token(scanner) == '!' ) return expr_parse_compare(scanner,str); else /* if the variant type is unresolved, cast to numeric zero */ { E_STATE(scanner)->type = EXPR_NUMERIC; g_free(str); return 0; } } switch((gint)g_scanner_get_next_token(scanner) ) { case '+': return expr_parse_num_value ( scanner, NULL ); case '-': return -expr_parse_num_value ( scanner, NULL ); case '!': return !expr_parse_num_value ( scanner, NULL ); case G_TOKEN_FLOAT: return scanner->value.v_float; case G_TOKEN_IDENT: return expr_parse_ident(scanner); case '(': val = expr_parse_num ( scanner, NULL ); parser_expect_symbol(scanner, ')',"(Number)"); return val; case G_TOKEN_IDENTIFIER: ptr = expr_parse_identifier(scanner); val = *ptr; g_free(ptr); return val; default: g_scanner_unexp_token(scanner,G_TOKEN_FLOAT,NULL,NULL,"","",TRUE); return 0; } } static gdouble expr_parse_num_factor ( GScanner *scanner, gdouble *prev ) { gdouble val; val = expr_parse_num_value ( scanner, prev ); while(strchr("*/%",g_scanner_peek_next_token ( scanner ))) { g_scanner_get_next_token ( scanner ); if(scanner->token == '*') val *= expr_parse_num_value( scanner, NULL ); if(scanner->token == '/') val /= expr_parse_num_value( scanner, NULL ); if(scanner->token == '%') val = (gint)val % (gint)expr_parse_num_value( scanner, NULL ); if(g_scanner_eof(scanner)) break; } return val; } static gdouble expr_parse_num_sum ( GScanner *scanner, gdouble *prev ) { gdouble val; val = expr_parse_num_factor ( scanner, prev ); while(strchr("+-",g_scanner_peek_next_token( scanner ))) { g_scanner_get_next_token (scanner ); if(scanner->token == '+') val+=expr_parse_num_factor( scanner, NULL ); if(scanner->token == '-') val-=expr_parse_num_factor( scanner, NULL ); if(g_scanner_eof(scanner)) break; } return val; } static gdouble expr_parse_num_compare ( GScanner *scanner, gdouble *prev ) { gdouble val; val = expr_parse_num_sum ( scanner, prev ); while(strchr("!<>=",g_scanner_peek_next_token ( scanner ))) { switch((gint)g_scanner_get_next_token ( scanner )) { case '>': if( g_scanner_peek_next_token( scanner ) == '=' ) { g_scanner_get_next_token( scanner ); val = (gdouble)(val >= expr_parse_num_sum ( scanner, NULL )); } else val = (gdouble)(val > expr_parse_num_sum ( scanner, NULL )); break; case '<': if( g_scanner_peek_next_token( scanner ) == '=' ) { g_scanner_get_next_token( scanner ); val = (gdouble)(val <= expr_parse_num_sum ( scanner, NULL )); } else val = (gdouble)(val < expr_parse_num_sum ( scanner, NULL )); break; case '=': val = (gdouble)(val == expr_parse_num_sum ( scanner, NULL )); break; case '!': if( g_scanner_peek_next_token( scanner ) != '=' ) g_scanner_unexp_token(scanner,'=',NULL,NULL,"","",TRUE); else { g_scanner_get_next_token(scanner); val = (gdouble)(val != expr_parse_num_sum ( scanner, NULL )); } break; } if(g_scanner_eof(scanner)) break; } return val; } static gdouble expr_parse_num( GScanner *scanner, gdouble *prev ) { gdouble val; gint istate; E_STATE(scanner)->type = EXPR_NUMERIC; val = expr_parse_num_compare ( scanner, prev ); while(strchr("&|",g_scanner_peek_next_token ( scanner ))) { istate = E_STATE(scanner)->ignore; switch((gint)g_scanner_get_next_token ( scanner )) { case '&': if(!val) E_STATE(scanner)->ignore = TRUE; val = expr_parse_num_compare ( scanner, NULL ) && val; break; case '|': if(val) E_STATE(scanner)->ignore = TRUE; val = expr_parse_num_compare ( scanner, NULL ) || val; break; } E_STATE(scanner)->ignore = istate; if(g_scanner_eof(scanner)) break; } E_STATE(scanner)->type = EXPR_NUMERIC; return val; } static gchar *expr_parse_root ( GScanner *scanner ) { gchar *str; gdouble res; if(E_STATE(scanner)->type == EXPR_NUMERIC || expr_is_numeric(scanner)) return expr_dtostr(expr_parse_num(scanner,NULL),-1); else if(E_STATE(scanner)->type == EXPR_STRING || expr_is_string(scanner)) str = expr_parse_str(scanner,NULL); else if(expr_is_variant(scanner)) str = expr_parse_variant(scanner); else return g_strdup(""); if(g_scanner_peek_next_token(scanner)=='=' || scanner->next_token == '!') { res = expr_parse_compare(scanner,str); return expr_dtostr(expr_parse_num(scanner,&res),-1); } if(E_STATE(scanner)->type != EXPR_STRING && strchr("|&<>=*/%!+-",g_scanner_peek_next_token(scanner))) { res = expr_str_to_num(str); return expr_dtostr(expr_parse_num(scanner,&res),-1); } return str; } void **expr_module_parameters ( GScanner *scanner, gchar *spec, gchar *name ) { void **params; gchar *value = NULL; gdouble numeric; gint i; parser_expect_symbol(scanner,'(',name); if(!spec) params = NULL; else { params = g_malloc0(strlen(spec)*sizeof(gpointer)); for(i=0;spec[i];i++) if(g_scanner_peek_next_token(scanner)!=')') { if(!value) { E_STATE(scanner)->type = EXPR_VARIANT; value = expr_parse_root(scanner); } if(g_ascii_tolower(spec[i])=='n' && E_STATE(scanner)->type!=EXPR_STRING) { numeric = expr_str_to_num(value); params[i] = g_memdup2(&numeric ,sizeof(gdouble)); value = NULL; } else if(g_ascii_tolower(spec[i])=='s' && E_STATE(scanner)->type!=EXPR_NUMERIC) { params[i] = value; value = NULL; } else if(!g_ascii_islower(spec[i])) g_scanner_error(scanner,"invalid type in parameter %d of %s",i,name); if(params[i] && g_scanner_peek_next_token(scanner)==',') g_scanner_get_next_token(scanner); } } parser_expect_symbol(scanner,')',name); return params; } static GScanner *expr_scanner_new ( void ) { GScanner *scanner; scanner = g_scanner_new(NULL); scanner->config->scan_octal = 0; scanner->config->symbol_2_token = 1; scanner->config->case_sensitive = 0; scanner->config->numbers_2_int = 1; scanner->config->int_2_float = 1; scanner->config->cset_identifier_nth = g_strconcat(".", scanner->config->cset_identifier_nth,NULL); scanner->config->cset_identifier_first = g_strconcat("$", scanner->config->cset_identifier_first,NULL); g_scanner_scope_add_symbol(scanner,0, "If", (gpointer)G_TOKEN_IF ); g_scanner_scope_add_symbol(scanner,0, "Cached", (gpointer)G_TOKEN_CACHED ); g_scanner_scope_add_symbol(scanner,0, "Lookup", (gpointer)G_TOKEN_LOOKUP ); g_scanner_scope_add_symbol(scanner,0, "Map", (gpointer)G_TOKEN_MAP ); g_scanner_scope_add_symbol(scanner,0, "Ident", (gpointer)G_TOKEN_IDENT ); g_scanner_set_scope(scanner,0); return scanner; } gchar *expr_parse( ExprCache *expr ) { GScanner *scanner; gchar *result; ExprState state; scanner = expr_scanner_new(); scanner->input_name = expr->definition; scanner->msg_handler = expr_print_msg; scanner->user_data = &state; E_STATE(scanner)->expr = expr; E_STATE(scanner)->type = EXPR_VARIANT; E_STATE(scanner)->error = FALSE; E_STATE(scanner)->ignore = FALSE; g_scanner_input_text(scanner, expr->definition, strlen(expr->definition)); result = expr_parse_root(scanner); if(g_scanner_peek_next_token(scanner) != G_TOKEN_EOF) g_scanner_error(scanner, "Unexpected input at the end of expression"); g_debug("expr: \"%s\" = \"%s\" (vstate: %d)",expr->definition,result, E_STATE(scanner)->expr->vstate); g_free(scanner->config->cset_identifier_nth); g_free(scanner->config->cset_identifier_first); g_scanner_destroy( scanner ); return result; } gboolean expr_cache_eval ( ExprCache *expr ) { gchar *eval; if(!expr || !expr->definition || !expr->eval) return FALSE; expr->vstate = FALSE; eval = expr_parse(expr); if(!expr->vstate) expr->eval = FALSE; if(g_strcmp0(eval,expr->cache)) { g_free(expr->cache); expr->cache = eval; return TRUE; } else { g_free(eval); return FALSE; } } ExprCache *expr_cache_new ( void ) { return g_malloc0(sizeof(ExprCache)); } void expr_cache_free ( ExprCache *expr ) { if(!expr) return; g_free(expr->definition); g_free(expr->cache); g_free(expr); } void expr_dep_add ( gchar *ident, ExprCache *expr ) { GList *list; ExprCache *iter; gchar *vname; if(!expr_deps) expr_deps = g_hash_table_new_full((GHashFunc)str_nhash, (GEqualFunc)str_nequal,g_free,NULL); vname = scanner_parse_identifier(ident,NULL); list = g_hash_table_lookup(expr_deps,vname); for(iter=expr;iter;iter=iter->parent) if(!g_list_find(list,iter)) list = g_list_prepend(list,iter); g_hash_table_replace(expr_deps,vname,list); } void expr_dep_trigger ( gchar *ident ) { GList *iter,*list; if(!expr_deps) return; list = g_hash_table_lookup(expr_deps,ident); for(iter=list;iter;iter=g_list_next(iter)) ((ExprCache *)(iter->data))->eval = TRUE; g_hash_table_remove(expr_deps,ident); g_list_free(list); } void expr_dep_dump_each ( void *key, void *value, void *d ) { GList *iter; for(iter=value;iter;iter=g_list_next(iter)) g_message("%s: %s", (gchar *)key, ((ExprCache *)iter->data)->definition); } void expr_dep_dump ( void ) { g_hash_table_foreach(expr_deps,expr_dep_dump_each,NULL); } sfwbar-1.0~beta13/src/expr.h000066400000000000000000000017251450657570000157700ustar00rootroot00000000000000#ifndef __EXPR_H__ #define __EXPR_H__ #include enum { G_TOKEN_IF = G_TOKEN_LAST + 1, G_TOKEN_CACHED, G_TOKEN_LOOKUP, G_TOKEN_MAP, G_TOKEN_IDENT }; enum { EXPR_STRING, EXPR_NUMERIC, EXPR_VARIANT }; typedef struct expr_cache { gchar *definition; gchar *cache; GtkWidget *widget; GdkEvent *event; gboolean eval; guint vstate; struct expr_cache *parent; } ExprCache; typedef struct expr_state { gint type; gboolean error; gboolean ignore; ExprCache *expr; } ExprState; #define E_STATE(x) ((ExprState *)x->user_data) gboolean expr_cache_eval ( ExprCache *expr ); void **expr_module_parameters ( GScanner *scanner, gchar *spec, gchar *name ); gchar *expr_dtostr ( double num, gint dec ); void expr_lib_init ( void ); ExprCache *expr_cache_new ( void ); void expr_cache_free ( ExprCache *expr ); void expr_dep_add ( gchar *ident, ExprCache *expr ); void expr_dep_trigger ( gchar *ident ); void expr_dep_dump ( void ); #endif sfwbar-1.0~beta13/src/exprlib.c000066400000000000000000000224631450657570000164540ustar00rootroot00000000000000/* This entire file is licensed under GNU General Public License v3.0 * * Copyright 2020- sfwbar maintainers */ #include #include #include "sfwbar.h" #include "module.h" #include "wintree.h" #include "expr.h" #include "basewidget.h" /* extract a substring */ static void *expr_lib_mid ( void **params, void *widget, void *event ) { gint len, c1, c2; if(!params || !params[0] || !params[1] || !params[2]) return g_strdup(""); c1 = *((gdouble *)params[1]); c2 = *((gdouble *)params[2]); len = strlen(params[0]); if(c1<0) /* negative offsets are relative to the end of the string */ c1+=len; if(c2<0) c2+=len; c1 = CLAMP(c1,0,len-1); c2 = CLAMP(c2,0,len-1); if(c1>c2) { c2^=c1; /* swap the ofsets */ c1^=c2; c2^=c1; } return g_strndup( params[0]+c1*sizeof(gchar), (c2-c1+1)*sizeof(gchar)); } /* Extract substring using regex */ static void *expr_lib_extract( void **params, void *widget, void *event ) { gchar *sres=NULL; GRegex *regex; GMatchInfo *match; if(!params || !params[0] || !params[1]) return g_strdup(""); regex = g_regex_new(params[1],0,0,NULL); if(regex && g_regex_match (regex, params[0], 0, &match) && match) { sres = g_match_info_fetch (match, 1); g_match_info_free (match); } if(regex) g_regex_unref (regex); return sres?sres:g_strdup(""); } static void *expr_lib_pad ( void **params, void *widget, void *event ) { gchar *result, *ptr; gint n, len, sign; gchar padchar; if(!params || !params[0] || !params[1]) return g_strdup(""); if( params[2] && *((gchar *)params[2]) ) padchar = *((gchar *)params[2]); else padchar = ' '; len = strlen(params[0]); n = (gint)*((gdouble *)params[1]); sign = n>=0; n = MAX(n>0?n:-n,len); result = g_malloc(n+1); if(sign) { memset(result, padchar, n-len); strcpy(result+n-len, params[0]); } else { ptr = g_stpcpy(result, params[0]); memset(ptr, padchar, n-len); *(result+n) = '\0'; } return result; } /* Get current time string */ static void *expr_lib_time ( void **params, void *widget, void *event ) { GTimeZone *tz; GDateTime *time; gchar *str; if(!params) return g_strdup(""); if(!params[1]) time = g_date_time_new_now_local(); else { #if GLIB_MAJOR_VERSION == 2 && GLIB_MINOR_VERSION >= 68 tz = g_time_zone_new_identifier(params[1]); #else tz = g_time_zone_new(params[1]); #endif time = g_date_time_new_now(tz); g_time_zone_unref(tz); } str = g_date_time_format (time, params[0]?params[0]:"%a %b %d %H:%M:%S %Y" ); g_date_time_unref(time); return str; } /* generate disk space utilization for a device */ static void *expr_lib_disk ( void **params, void *widget, void *event ) { struct statvfs fs; gdouble *result = g_malloc0(sizeof(gdouble)); if(!params || !params[0] || !params[1]) return result; if(statvfs(params[0],&fs)) return result; if(!g_ascii_strcasecmp(params[1],"total")) *result = fs.f_blocks * fs.f_frsize; if(!g_ascii_strcasecmp(params[1],"avail")) *result = fs.f_bavail * fs.f_bsize; if(!g_ascii_strcasecmp(params[1],"free")) *result = fs.f_bfree * fs.f_bsize; if(!g_ascii_strcasecmp(params[1],"%avail")) *result = ((gdouble)(fs.f_bfree*fs.f_bsize) / (gdouble)(fs.f_blocks*fs.f_frsize))*100; if(!g_ascii_strcasecmp(params[1],"%used")) *result = (1.0 - (gdouble)(fs.f_bfree*fs.f_bsize) / (gdouble)(fs.f_blocks*fs.f_frsize))*100; return result; } static void *expr_lib_active ( void **params, void *widget, void *event ) { return g_strdup(wintree_get_active()); } static void *expr_lib_str ( void **params, void *widget, void *event ) { if(!params || !params[0]) return g_strdup(""); else return expr_dtostr(*(gdouble *)params[0], params[1]?(gint)*(gdouble *)params[1]:0); } static void *expr_lib_max ( void **params, void *widget, void *event ) { gdouble *result; if(!params || !params[0] || !params[1]) return g_malloc0(sizeof(gdouble)); result = g_malloc(sizeof(gdouble)); *result = MAX(*(gdouble *)params[0],*(gdouble *)params[1]); return result; } static void *expr_lib_min ( void **params, void *widget, void *event ) { gdouble *result; if(!params || !params[0] || !params[1]) return g_malloc0(sizeof(gdouble)); result = g_malloc(sizeof(gdouble)); *result = MIN(*(gdouble *)params[0],*(gdouble *)params[1]); return result; } static void *expr_lib_val ( void **params, void *widget, void *event ) { gdouble *result; if(!params || !params[0]) return g_malloc0(sizeof(gdouble)); result = g_malloc(sizeof(gdouble)); *result = strtod(params[0],NULL); return result; } static void *expr_lib_upper ( void **params, void *widget, void *event ) { if(!params || !params[0]) return g_strdup(""); else return g_ascii_strup(params[0],-1); } static void *expr_lib_lower ( void **params, void *widget, void *event ) { if(!params || !params[0]) return g_strdup(""); else return g_ascii_strdown(params[0],-1); } static void *expr_lib_gtkevent ( void **params, void *base, void *event ) { GtkWidget *widget; GdkEventButton *ev = event; GtkAllocation alloc; GtkStyleContext *style; GtkBorder margin, padding, border; gint x,y,w,h,dir; gdouble *result; if(!params || !params[0]) return g_malloc0(sizeof(gdouble)); if(GTK_IS_BIN(base)) { widget = gtk_bin_get_child(base); gtk_widget_translate_coordinates(base,widget,ev->x,ev->y,&x,&y); } else { widget = base; x = ev->x; y = ev->y; } if(!g_ascii_strcasecmp(params[0],"x")) dir = GTK_POS_RIGHT; else if(!g_ascii_strcasecmp(params[0],"y")) dir = GTK_POS_BOTTOM; else if(!g_ascii_strcasecmp(params[0],"dir")) gtk_widget_style_get(widget,"direction",&dir,NULL); else return g_malloc0(sizeof(gdouble)); gtk_widget_get_allocation( widget, &alloc ); style = gtk_widget_get_style_context(widget); gtk_style_context_get_margin(style,gtk_style_context_get_state(style),&margin); gtk_style_context_get_padding(style,gtk_style_context_get_state(style),&padding); gtk_style_context_get_border(style,gtk_style_context_get_state(style),&border); w = alloc.width - margin.left - margin.right - padding.left - padding.right - border.left - border.right; h = alloc.height - margin.top - margin.bottom - padding.top - padding.bottom - border.top - border.bottom; x = x - margin.left - padding.left - border.left; y = y - margin.top - padding.top - border.top; result = g_malloc0(sizeof(gdouble)); if(dir==GTK_POS_RIGHT || dir==GTK_POS_LEFT) *result = CLAMP((gdouble)x / w,0,1); else *result = CLAMP((gdouble)y / h,0,1); if(dir==GTK_POS_LEFT || dir==GTK_POS_TOP) *result = 1.0 - *result; return result; } static void *expr_lib_widget_id ( void **params, void *widget, void *event ) { gchar *id = base_widget_get_id(widget); return g_strdup(id?id:""); } ModuleExpressionHandlerV1 mid_handler = { .flags = MODULE_EXPR_DETERMINISTIC, .name = "mid", .parameters = "SNN", .function = expr_lib_mid }; ModuleExpressionHandlerV1 pad_handler = { .flags = MODULE_EXPR_DETERMINISTIC, .name = "pad", .parameters = "SNs", .function = expr_lib_pad }; ModuleExpressionHandlerV1 extract_handler = { .flags = MODULE_EXPR_DETERMINISTIC, .name = "extract", .parameters = "SS", .function = expr_lib_extract }; ModuleExpressionHandlerV1 time_handler = { .flags = 0, .name = "time", .parameters = "ss", .function = expr_lib_time }; ModuleExpressionHandlerV1 disk_handler = { .flags = MODULE_EXPR_NUMERIC, .name = "disk", .parameters = "SS", .function = expr_lib_disk }; ModuleExpressionHandlerV1 active_handler = { .flags = 0, .name = "ActiveWin", .parameters = "", .function = expr_lib_active }; ModuleExpressionHandlerV1 str_handler = { .flags = MODULE_EXPR_DETERMINISTIC, .name = "str", .parameters = "Nn", .function = expr_lib_str }; ModuleExpressionHandlerV1 max_handler = { .flags = MODULE_EXPR_DETERMINISTIC | MODULE_EXPR_NUMERIC, .name = "max", .parameters = "NN", .function = expr_lib_max }; ModuleExpressionHandlerV1 min_handler = { .flags = MODULE_EXPR_DETERMINISTIC | MODULE_EXPR_NUMERIC, .name = "min", .parameters = "NN", .function = expr_lib_min }; ModuleExpressionHandlerV1 val_handler = { .flags = MODULE_EXPR_NUMERIC | MODULE_EXPR_DETERMINISTIC, .name = "val", .parameters = "S", .function = expr_lib_val }; ModuleExpressionHandlerV1 upper_handler = { .flags = MODULE_EXPR_DETERMINISTIC, .name = "upper", .parameters = "S", .function = expr_lib_upper }; ModuleExpressionHandlerV1 lower_handler = { .flags = MODULE_EXPR_DETERMINISTIC, .name = "lower", .parameters = "S", .function = expr_lib_lower }; ModuleExpressionHandlerV1 gtkevent_handler = { .flags = MODULE_EXPR_NUMERIC, .name = "gtkevent", .parameters = "S", .function = expr_lib_gtkevent }; ModuleExpressionHandlerV1 widget_id_handler = { .name = "widgetid", .parameters = "", .function = expr_lib_widget_id }; ModuleExpressionHandlerV1 *expr_lib_handlers[] = { &mid_handler, &pad_handler, &extract_handler, &time_handler, &disk_handler, &active_handler, &min_handler, &max_handler, &str_handler, &val_handler, &upper_handler, &lower_handler, >kevent_handler, &widget_id_handler, NULL }; void expr_lib_init ( void ) { module_expr_funcs_add(expr_lib_handlers,"expression library"); } sfwbar-1.0~beta13/src/flowgrid.c000066400000000000000000000274311450657570000166240ustar00rootroot00000000000000/* This entire file is licensed under GNU General Public License v3.0 * * Copyright 2021- sfwbar maintainers */ #include "sfwbar.h" #include "flowgrid.h" #include "basewidget.h" #include "taskbargroup.h" #include "config.h" #include "window.h" #include "bar.h" G_DEFINE_TYPE_WITH_CODE (FlowGrid, flow_grid, GTK_TYPE_GRID, G_ADD_PRIVATE(FlowGrid)); static void flow_grid_get_preferred_width (GtkWidget *widget, gint *minimal, gint *natural) { FlowGridPrivate *priv; g_return_if_fail(widget != NULL); g_return_if_fail(IS_FLOW_GRID(widget)); priv = flow_grid_get_instance_private(FLOW_GRID(widget)); GTK_WIDGET_CLASS(flow_grid_parent_class)->get_preferred_width( widget, minimal, natural); if(priv->rows>0 && priv->limit ) *minimal = MIN(*natural, 1); } static void flow_grid_get_preferred_height (GtkWidget *widget, gint *minimal, gint *natural) { FlowGridPrivate *priv; g_return_if_fail(widget != NULL); g_return_if_fail(IS_FLOW_GRID(widget)); priv = flow_grid_get_instance_private(FLOW_GRID(widget)); GTK_WIDGET_CLASS(flow_grid_parent_class)->get_preferred_height( widget, minimal, natural); if(priv->cols>0 && priv->limit) *minimal = MIN(*natural, 1); } static void flow_grid_destroy( GtkWidget *self ) { FlowGridPrivate *priv; g_return_if_fail(IS_FLOW_GRID(self)); priv = flow_grid_get_instance_private(FLOW_GRID(self)); g_clear_pointer(&priv->dnd_target,gtk_target_entry_free); g_list_free_full(g_steal_pointer(&priv->children), (GDestroyNotify)gtk_widget_destroy); GTK_WIDGET_CLASS(flow_grid_parent_class)->destroy(self); } static void flow_grid_class_init ( FlowGridClass *kclass ) { GtkWidgetClass *widget_class = GTK_WIDGET_CLASS(kclass); widget_class->get_preferred_width = flow_grid_get_preferred_width; widget_class->get_preferred_height = flow_grid_get_preferred_height; widget_class->destroy = flow_grid_destroy; } static void flow_grid_init ( FlowGrid *self ) { FlowGridPrivate *priv; gchar *sig; g_return_if_fail(IS_FLOW_GRID(self)); priv = flow_grid_get_instance_private(FLOW_GRID(self)); priv->rows = 1; priv->cols = 0; priv->limit = TRUE; sig = g_strdup_printf("flow-item-%p", self); priv->dnd_target = gtk_target_entry_new(sig, 0, 1); g_free(sig); gtk_grid_set_row_homogeneous(GTK_GRID(self), TRUE); gtk_grid_set_column_homogeneous(GTK_GRID(self), TRUE); } GtkWidget *flow_grid_new( gboolean limit) { GtkWidget *w; FlowGridPrivate *priv; w = GTK_WIDGET(g_object_new(flow_grid_get_type(), NULL)); priv = flow_grid_get_instance_private(FLOW_GRID(w)); flow_grid_set_sort(w, TRUE); priv->limit = limit; return w; } void flow_grid_set_cols ( GtkWidget *cgrid, gint cols ) { FlowGridPrivate *priv; g_return_if_fail(cgrid != NULL); g_return_if_fail(IS_FLOW_GRID(cgrid)); priv = flow_grid_get_instance_private(FLOW_GRID(cgrid)); priv->cols = cols; priv->rows = 0; if((priv->rows<1)&&(priv->cols<1)) priv->rows = 1; } void flow_grid_set_rows ( GtkWidget *cgrid, gint rows ) { FlowGridPrivate *priv; g_return_if_fail(cgrid != NULL); g_return_if_fail(IS_FLOW_GRID(cgrid)); priv = flow_grid_get_instance_private(FLOW_GRID(cgrid)); priv->rows = rows; priv->cols = 0; if((priv->rows<1)&&(priv->cols<1)) priv->rows = 1; } void flow_grid_set_primary ( GtkWidget *self, gint primary ) { FlowGridPrivate *priv; g_return_if_fail(IS_FLOW_GRID(self)); priv = flow_grid_get_instance_private(FLOW_GRID(self)); priv->primary_axis = primary; } void flow_grid_set_sort ( GtkWidget *cgrid, gboolean sort ) { FlowGridPrivate *priv; g_return_if_fail(cgrid != NULL); g_return_if_fail(IS_FLOW_GRID(cgrid)); priv = flow_grid_get_instance_private(FLOW_GRID(cgrid)); priv->sort = sort; } void flow_grid_remove_widget ( GtkWidget *widget, GtkWidget *parent ) { gtk_container_remove ( GTK_CONTAINER(parent), widget ); } void flow_grid_invalidate ( GtkWidget *self ) { FlowGridPrivate *priv; if(IS_BASE_WIDGET(self)) self = base_widget_get_child(self); g_return_if_fail(IS_FLOW_GRID(self)); priv = flow_grid_get_instance_private(FLOW_GRID(self)); priv->invalid = TRUE; } void flow_grid_add_child ( GtkWidget *self, GtkWidget *child ) { FlowGridPrivate *priv; if(IS_BASE_WIDGET(self)) self = base_widget_get_child(self); g_return_if_fail(IS_FLOW_GRID(self)); priv = flow_grid_get_instance_private(FLOW_GRID(self)); priv->children = g_list_append(priv->children,child); flow_item_set_parent(child, self); flow_grid_invalidate(self); } void flow_grid_delete_child ( GtkWidget *self, void *parent ) { FlowGridPrivate *priv; GList *iter; GCompareFunc comp_parent; if(IS_BASE_WIDGET(self)) self = base_widget_get_child(self); g_return_if_fail(IS_FLOW_GRID(self)); priv = flow_grid_get_instance_private(FLOW_GRID(self)); if(!priv->children || !priv->children->data || !FLOW_ITEM_GET_CLASS(priv->children->data)->comp_parent) return; comp_parent = FLOW_ITEM_GET_CLASS(priv->children->data)->comp_parent; for(iter=priv->children;iter;iter=g_list_next(iter)) if(!comp_parent(flow_item_get_parent(iter->data),parent)) { gtk_widget_destroy(iter->data); priv->children = g_list_delete_link(priv->children,iter); break; } flow_grid_invalidate(self); } void flow_grid_update ( GtkWidget *self ) { FlowGridPrivate *priv; GList *iter; gint count, i, cols, rows; if(IS_BASE_WIDGET(self)) self = base_widget_get_child(self); g_return_if_fail(IS_FLOW_GRID(self)); priv = flow_grid_get_instance_private(FLOW_GRID(self)); if(!priv->invalid) return; priv->invalid = FALSE; if(!priv->primary_axis) { if(priv->rows>0) priv->primary_axis = G_TOKEN_COLS; else priv->primary_axis = G_TOKEN_ROWS; } gtk_container_foreach(GTK_CONTAINER(self), (GtkCallback)flow_grid_remove_widget,self); if(priv->sort) priv->children = g_list_sort_with_data(priv->children, (GCompareDataFunc)flow_item_compare,self); count = 0; for(iter=priv->children;iter;iter=g_list_next(iter)) { flow_item_update(iter->data); if(flow_item_get_active(iter->data)) count++; } rows = 0; cols = 0; if(priv->rows>0) { if(priv->primary_axis == G_TOKEN_COLS) rows = priv->rows; else cols = (count/priv->rows) + ((count%priv->rows)?1:0); } else { if(priv->primary_axis == G_TOKEN_ROWS) cols = priv->cols; else rows = (count/priv->cols) + ((count%priv->cols)?1:0); } i = 0; for(iter=priv->children; iter; iter=g_list_next(iter)) if(flow_item_get_active(iter->data)) { if(rows>0) gtk_grid_attach(GTK_GRID(self), iter->data, i/rows, i%rows, 1, 1); else if(cols>0) gtk_grid_attach(GTK_GRID(self), iter->data, i%cols, i/cols, 1, 1); i++; } if(rows>0) for(;ichildren); } gpointer flow_grid_find_child ( GtkWidget *self, gconstpointer parent ) { FlowGridPrivate *priv; GList *iter; GCompareFunc comp_parent; if(IS_BASE_WIDGET(self)) self = base_widget_get_child(self); g_return_val_if_fail(IS_FLOW_GRID(self),NULL); priv = flow_grid_get_instance_private(FLOW_GRID(self)); if(!priv->children || !priv->children->data) return NULL; comp_parent = FLOW_ITEM_GET_CLASS(priv->children->data)->comp_parent; if(!comp_parent) return NULL; for(iter=priv->children; iter; iter=g_list_next(iter)) if(!comp_parent(flow_item_get_parent(iter->data), parent)) return iter->data; return NULL; } static void flow_grid_dnd_data_rec_cb ( GtkWidget *dest, GdkDragContext *ctx, gint x, gint y, GtkSelectionData *sel, guint info, guint time, gpointer parent ) { FlowGridPrivate *priv; GtkWidget *src; GtkAllocation alloc; GList *dlist; gboolean after; if(IS_BASE_WIDGET(parent)) parent = base_widget_get_child(parent); g_return_if_fail(IS_FLOW_GRID(parent)); src = *(GtkWidget **)gtk_selection_data_get_data(sel); if(src==dest) return; priv = flow_grid_get_instance_private(FLOW_GRID(parent)); gtk_widget_get_allocation( dest, &alloc ); after = ((priv->cols>0 && y>alloc.height/2) || (priv->rows>0 && x>alloc.width/2)); dlist = g_list_find(priv->children,dest); if(!dlist) return; priv->children = g_list_remove(priv->children, src ); priv->children = g_list_insert_before(priv->children, after ? g_list_next(dlist) : dlist, src ); flow_item_invalidate(dest); } static void flow_grid_dnd_enter_cb ( GtkWidget *widget, GdkEventCrossing *ev, gpointer data ) { bar_sensor_cancel_hide(gtk_widget_get_ancestor(widget, GTK_TYPE_WINDOW)); } static void flow_grid_dnd_begin_cb ( GtkWidget *widget, GdkDragContext *ctx, gpointer data ) { g_signal_handlers_unblock_matched(widget, G_SIGNAL_MATCH_FUNC, 0,0 ,NULL, (GFunc)flow_grid_dnd_enter_cb,NULL); gtk_grab_add(widget); window_ref(gtk_widget_get_ancestor(widget, GTK_TYPE_WINDOW), widget); } static void flow_grid_dnd_end_cb ( GtkWidget *widget, GdkDragContext *ctx, gpointer data ) { g_signal_handlers_block_matched(widget, G_SIGNAL_MATCH_FUNC, 0,0 ,NULL, (GFunc)flow_grid_dnd_enter_cb,NULL); gtk_grab_remove(widget); window_unref(widget, gtk_widget_get_ancestor(data, GTK_TYPE_WINDOW)); } static void flow_grid_dnd_data_get_cb ( GtkWidget *widget, GdkDragContext *ctx, GtkSelectionData *sel, guint info, guint time, gpointer *data ) { gtk_selection_data_set(sel,gdk_atom_intern_static_string("gpointer"), 8,(const guchar *)&data,sizeof(gpointer)); } void flow_grid_child_dnd_enable ( GtkWidget *self, GtkWidget *child, GtkWidget *src ) { FlowGridPrivate *priv; g_return_if_fail(IS_FLOW_ITEM(child)); if(IS_BASE_WIDGET(self)) self = base_widget_get_child(self); g_return_if_fail(IS_FLOW_GRID(self)); priv = flow_grid_get_instance_private(FLOW_GRID(self)); if(!priv->dnd_target) return; gtk_drag_source_set(src, GDK_BUTTON1_MASK, priv->dnd_target, 1, GDK_ACTION_MOVE); g_signal_connect(G_OBJECT(src),"drag-data-get", G_CALLBACK(flow_grid_dnd_data_get_cb),child); gtk_drag_dest_set(child, GTK_DEST_DEFAULT_ALL, priv->dnd_target, 1, GDK_ACTION_MOVE); g_signal_connect(G_OBJECT(child), "drag-data-received", G_CALLBACK(flow_grid_dnd_data_rec_cb), self); g_signal_connect(G_OBJECT(src), "drag-begin", G_CALLBACK(flow_grid_dnd_begin_cb), self); g_signal_connect(G_OBJECT(src), "drag-end", G_CALLBACK(flow_grid_dnd_end_cb), self); g_signal_connect(G_OBJECT(src), "enter-notify-event", G_CALLBACK(flow_grid_dnd_enter_cb), NULL); g_signal_handlers_block_matched(src, G_SIGNAL_MATCH_FUNC, 0,0 ,NULL, (GFunc)flow_grid_dnd_enter_cb,NULL); } void flow_grid_copy_properties ( GtkWidget *dest, GtkWidget *src ) { FlowGridPrivate *spriv, *dpriv; g_return_if_fail( IS_BASE_WIDGET(src) && IS_BASE_WIDGET(dest) ); g_return_if_fail( IS_FLOW_GRID(base_widget_get_child(src)) && IS_FLOW_GRID(base_widget_get_child(dest)) ); spriv = flow_grid_get_instance_private(FLOW_GRID( base_widget_get_child(src))); dpriv = flow_grid_get_instance_private(FLOW_GRID( base_widget_get_child(dest))); dpriv->rows = spriv->rows; dpriv->cols = spriv->cols; dpriv->sort = spriv->sort; dpriv->primary_axis = spriv->primary_axis; g_object_set_data(G_OBJECT(dest), "icons", g_object_get_data(G_OBJECT(src), "icons")); g_object_set_data(G_OBJECT(dest), "labels", g_object_get_data(G_OBJECT(src), "labels")); } sfwbar-1.0~beta13/src/flowgrid.h000066400000000000000000000033501450657570000166230ustar00rootroot00000000000000#ifndef __FLOWGRID_H__ #define __FLOWGRID_H__ #include #include "flowitem.h" #define FLOW_GRID_TYPE (flow_grid_get_type()) #define FLOW_GRID(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), FLOW_GRID_TYPE, FlowGrid)) #define FLOW_GRID_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), FLOW_GRID_TYPE, FlowGridClass)) #define IS_FLOW_GRID(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), FLOW_GRID_TYPE)) #define IS_FLOW_GRIDCLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), FLOW_GRID_TYPE)) typedef struct _FlowGrid FlowGrid; typedef struct _FlowGridClass FlowGridClass; struct _FlowGrid { GtkGrid grid; }; struct _FlowGridClass { GtkGridClass parent_class; }; typedef struct _FlowGridPrivate FlowGridPrivate; struct _FlowGridPrivate { gint cols,rows; gint primary_axis; gboolean limit; gboolean invalid; gboolean sort; GList *children; gint (*comp)( GtkWidget *, GtkWidget *, GtkWidget * ); GtkTargetEntry *dnd_target; }; GType flow_grid_get_type ( void ); GtkWidget *flow_grid_new( gboolean limit); void flow_grid_set_rows ( GtkWidget *cgrid, gint rows ); void flow_grid_set_cols ( GtkWidget *cgrid, gint cols ); void flow_grid_set_primary ( GtkWidget *self, gint primary ); void flow_grid_add_child ( GtkWidget *self, GtkWidget *child ); void flow_grid_update ( GtkWidget *self ); void flow_grid_invalidate ( GtkWidget *self ); void flow_grid_delete_child ( GtkWidget *, void *parent ); guint flow_grid_n_children ( GtkWidget *self ); gpointer flow_grid_find_child ( GtkWidget *, gconstpointer parent ); void flow_grid_child_dnd_enable ( GtkWidget *, GtkWidget *, GtkWidget *); void flow_grid_set_sort ( GtkWidget *cgrid, gboolean sort ); void flow_grid_copy_properties ( GtkWidget *dest, GtkWidget *src ); #endif sfwbar-1.0~beta13/src/flowitem.c000066400000000000000000000047071450657570000166360ustar00rootroot00000000000000/* This entire file is licensed under GNU General Public License v3.0 * * Copyright 2022- sfwbar maintainers */ #include "sfwbar.h" #include "flowitem.h" #include "basewidget.h" #include "flowgrid.h" G_DEFINE_TYPE_WITH_CODE(FlowItem, flow_item, BASE_WIDGET_TYPE, G_ADD_PRIVATE(FlowItem)); static gint flow_item_comp_parent ( gconstpointer p1, gconstpointer p2 ) { return p2 - p1; } static void flow_item_destroy ( GtkWidget *self ) { FlowItemPrivate *priv; g_return_if_fail(IS_FLOW_ITEM(self)); priv = flow_item_get_instance_private(FLOW_ITEM(self)); flow_grid_delete_child(priv->parent,self); GTK_WIDGET_CLASS(flow_item_parent_class)->destroy(self); } static void flow_item_class_init ( FlowItemClass *kclass ) { FLOW_ITEM_CLASS(kclass)->comp_parent = flow_item_comp_parent; GTK_WIDGET_CLASS(kclass)->destroy = flow_item_destroy; } void flow_item_set_parent ( GtkWidget *self, GtkWidget *parent ) { FlowItemPrivate *priv; g_return_if_fail(IS_FLOW_ITEM(self)); priv = flow_item_get_instance_private(FLOW_ITEM(self)); priv->parent = parent; } void flow_item_set_active ( GtkWidget *self, gboolean active ) { FlowItemPrivate *priv; g_return_if_fail(IS_FLOW_ITEM(self)); priv = flow_item_get_instance_private(FLOW_ITEM(self)); priv->active = active; } gboolean flow_item_get_active ( GtkWidget *self ) { FlowItemPrivate *priv; g_return_val_if_fail(IS_FLOW_ITEM(self),FALSE); priv = flow_item_get_instance_private(FLOW_ITEM(self)); return priv->active; } static void flow_item_init ( FlowItem *self ) { flow_item_set_active(GTK_WIDGET(self),TRUE); } void flow_item_update ( GtkWidget *self ) { g_return_if_fail(IS_FLOW_ITEM(self)); if(FLOW_ITEM_GET_CLASS(self)->update) FLOW_ITEM_GET_CLASS(self)->update(self); } void flow_item_invalidate ( GtkWidget *self ) { if(!self) return; g_return_if_fail(IS_FLOW_ITEM(self)); if(FLOW_ITEM_GET_CLASS(self)->invalidate) FLOW_ITEM_GET_CLASS(self)->invalidate(self); } void *flow_item_get_parent ( GtkWidget *self ) { g_return_val_if_fail(IS_FLOW_ITEM(self),NULL); if(FLOW_ITEM_GET_CLASS(self)->get_parent) return FLOW_ITEM_GET_CLASS(self)->get_parent(self); else return NULL; } gint flow_item_compare ( GtkWidget *p1, GtkWidget *p2, GtkWidget *parent ) { g_return_val_if_fail(IS_FLOW_ITEM(p1),0); g_return_val_if_fail(IS_FLOW_ITEM(p2),0); if(!FLOW_ITEM_GET_CLASS(p1)->compare) return 0; return FLOW_ITEM_GET_CLASS(p1)->compare(p1,p2,parent); } sfwbar-1.0~beta13/src/flowitem.h000066400000000000000000000030341450657570000166330ustar00rootroot00000000000000#ifndef __FLOWITEM_H__ #define __FLOWITEM_H__ #include #include "basewidget.h" #define FLOW_ITEM_TYPE (flow_item_get_type()) #define FLOW_ITEM_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), FLOW_ITEM_TYPE, FlowItemClass)) #define FLOW_ITEM(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), FLOW_ITEM_TYPE, FlowItem)) #define IS_FLOW_ITEM(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), FLOW_ITEM_TYPE)) #define IS_FLOW_TEMCLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), FLOW_ITEM_TYPE)) #define FLOW_ITEM_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), FLOW_ITEM_TYPE, FlowItemClass)) typedef struct _FlowItem FlowItem; typedef struct _FlowItemClass FlowItemClass; struct _FlowItem { BaseWidget widget; }; struct _FlowItemClass { BaseWidgetClass parent_class; void (*update) ( GtkWidget *self ); void (*invalidate) ( GtkWidget *self ); void* (*get_parent) ( GtkWidget *self ); gint (*compare) (GtkWidget *, GtkWidget *, GtkWidget *); GCompareFunc comp_parent; }; typedef struct _FlowItemPrivate FlowItemPrivate; struct _FlowItemPrivate { gboolean active; GtkWidget *parent; }; GType flow_item_get_type ( void ); void flow_item_update ( GtkWidget *self ); void flow_item_invalidate ( GtkWidget *self ); void *flow_item_get_parent ( GtkWidget *self ); void flow_item_set_parent ( GtkWidget *self, GtkWidget *parent ); void flow_item_set_active ( GtkWidget *self, gboolean ); gboolean flow_item_get_active ( GtkWidget *self ); gint flow_item_compare ( GtkWidget *p1, GtkWidget *p2, GtkWidget *parent ); #endif sfwbar-1.0~beta13/src/grid.c000066400000000000000000000073451450657570000157360ustar00rootroot00000000000000/* This entire file is licensed under GNU General Public License v3.0 * * Copyright 2022- sfwbar maintainers */ #include "sfwbar.h" #include "basewidget.h" #include "grid.h" G_DEFINE_TYPE_WITH_CODE (Grid, grid, BASE_WIDGET_TYPE, G_ADD_PRIVATE (Grid)); static void grid_destroy ( GtkWidget *self ) { GridPrivate *priv; priv = grid_get_instance_private(GRID(self)); g_list_free(g_steal_pointer(&priv->children)); g_list_free(g_steal_pointer(&priv->last)); GTK_WIDGET_CLASS(grid_parent_class)->destroy(self); } static GtkWidget *grid_get_child ( GtkWidget *self ) { GridPrivate *priv; g_return_val_if_fail(IS_GRID(self),NULL); priv = grid_get_instance_private(GRID(self)); return priv->grid; } static GtkWidget *grid_mirror ( GtkWidget *src ) { GtkWidget *self, *grid, *item; GdkRectangle rect; GList *children,*iter; self = grid_new(); base_widget_copy_properties(self,src); grid = base_widget_get_child(src); children = gtk_container_get_children(GTK_CONTAINER(grid)); for(iter=children;iter;iter=g_list_next(iter)) { item = base_widget_mirror(iter->data); if(item) { gtk_container_child_get(GTK_CONTAINER(grid),iter->data, "left-attach",&rect.x, "top-attach",&rect.y, "width",&rect.width, "height",&rect.height, NULL); base_widget_set_rect(item,rect); grid_attach(self,item); } } g_list_free(children); return self; } static void grid_child_park ( GtkWidget *widget, GtkWidget *grid ) { g_object_ref(widget); gtk_container_remove(GTK_CONTAINER(grid),widget); } static void grid_style_updated ( GtkWidget *grid, GtkWidget *self ) { GridPrivate *priv; gint dir; GList *iter; priv = grid_get_instance_private(GRID(self)); gtk_widget_style_get(priv->grid,"direction",&dir,NULL); if(priv->dir == dir) return; priv->dir = dir; gtk_container_foreach(GTK_CONTAINER(priv->grid), (GtkCallback)grid_child_park,priv->grid); g_list_free(g_steal_pointer(&priv->last)); for(iter=priv->children;iter;iter=g_list_next(iter)) { grid_attach(self,iter->data); g_object_unref(iter->data); } } static void grid_remove ( GtkWidget *grid, GtkWidget *child, GtkWidget *self ) { GridPrivate *priv; priv = grid_get_instance_private(GRID(self)); priv->last = g_list_remove(priv->last, child); } static void grid_class_init ( GridClass *kclass ) { GTK_WIDGET_CLASS(kclass)->destroy = grid_destroy; BASE_WIDGET_CLASS(kclass)->get_child = grid_get_child; BASE_WIDGET_CLASS(kclass)->mirror = grid_mirror; } static void grid_init ( Grid *self ) { } GtkWidget *grid_new ( void ) { GtkWidget *self; GridPrivate *priv; self = GTK_WIDGET(g_object_new(grid_get_type(), NULL)); priv = grid_get_instance_private(GRID(self)); priv->grid = gtk_grid_new(); gtk_container_add(GTK_CONTAINER(self),priv->grid); g_signal_connect(G_OBJECT(priv->grid),"style_updated", (GCallback)grid_style_updated,self); g_signal_connect(G_OBJECT(priv->grid),"remove", (GCallback)grid_remove,self); return self; } static void grid_detach( GtkWidget *child, GtkWidget *self ) { GridPrivate *priv; priv = grid_get_instance_private(GRID(self)); priv->children = g_list_remove(priv->children, child); priv->last = g_list_remove(priv->last, child); } void grid_attach ( GtkWidget *self, GtkWidget *child ) { GridPrivate *priv; g_return_if_fail(IS_GRID(self)); priv = grid_get_instance_private(GRID(self)); base_widget_attach(priv->grid, child, priv->last?priv->last->data:NULL); if(!g_list_find(priv->children,child)) { priv->children = g_list_append(priv->children,child); priv->last = g_list_prepend(priv->last, child); g_signal_connect(G_OBJECT(child), "destroy", (GCallback)grid_detach, self); } } sfwbar-1.0~beta13/src/grid.h000066400000000000000000000015101450657570000157270ustar00rootroot00000000000000#ifndef __GRID_H__ #define __GRID_H__ #include "basewidget.h" #define GRID_TYPE (grid_get_type()) #define GRID(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), GRID_TYPE, Grid)) #define GRID_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), GRID_TYPE, GridClass)) #define IS_GRID(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), GRID_TYPE)) #define IS_GRIDCLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), GRID_TYPE)) typedef struct _Grid Grid; typedef struct _GridClass GridClass; struct _Grid { BaseWidget item; }; struct _GridClass { BaseWidgetClass parent_class; }; typedef struct _GridPrivate GridPrivate; struct _GridPrivate { GtkWidget *grid; GList *last; GList *children; gint dir; }; GType grid_get_type ( void ); GtkWidget *grid_new(); void grid_attach ( GtkWidget *self, GtkWidget *child ); #endif sfwbar-1.0~beta13/src/hypr_ipc.c000066400000000000000000000353121450657570000166210ustar00rootroot00000000000000/* This entire file is licensed under GNU General Public License v3.0 * * Copyright 2022- sfwbar maintainers */ #include "sfwbar.h" #include "wintree.h" #include "pager.h" #include #define hypr_ipc_parse_id(x) GSIZE_TO_POINTER(g_ascii_strtoull(x,NULL,16)) static gchar *ipc_sockaddr; static gpointer hypr_ipc_window_id ( json_object *json ) { const gchar *str; str = json_string_by_name(json,"address"); if(!str) return NULL; return hypr_ipc_parse_id(str); } static gpointer hypr_ipc_workspace_id ( json_object *json ) { json_object *ptr; if(json_object_object_get_ex(json,"workspace",&ptr) && ptr) return GINT_TO_POINTER(json_int_by_name(ptr,"id",0)); return NULL; } static gboolean hypr_ipc_window_geom ( json_object *json, GdkRectangle *res ) { json_object *ptr; if(!json_object_object_get_ex(json,"at",&ptr) || !ptr) return FALSE; res->x = json_object_get_int(json_object_array_get_idx(ptr,0)); res->y = json_object_get_int(json_object_array_get_idx(ptr,1)); if(!json_object_object_get_ex(json,"size",&ptr) || !ptr) return FALSE; res->width = json_object_get_int(json_object_array_get_idx(ptr,0)); res->height = json_object_get_int(json_object_array_get_idx(ptr,1)); return TRUE; } static gboolean hypr_ipc_request ( gchar *addr, gchar *command, json_object **json ) { gint sock; if(!command) return FALSE; sock = socket_connect(addr,1000); if(sock==-1) { g_debug("hypr: can't open socket"); return FALSE; } if(write(sock,command,strlen(command))==-1) { g_debug("hypr: can't write to socket"); close(sock); return FALSE; } if(json) *json = recv_json(sock,-1); close(sock); return TRUE; } static void hypr_ipc_command ( gchar *cmd, ... ) { va_list args; gchar *buf; if(!cmd) return; va_start(args,cmd); buf = g_strdup_vprintf(cmd,args); g_debug("hypr command: %s",buf); if(!hypr_ipc_request ( ipc_sockaddr, buf, NULL)) g_debug("hypr: unable to send command"); g_free(buf); va_end(args); } static gchar *hypr_ipc_workspace_data ( gpointer id, gchar *field ) { json_object *json,*ptr; gint i; gchar *res = NULL; if(!hypr_ipc_request(ipc_sockaddr,"j/workspaces",&json) || !json) return res; if(json_object_is_type(json, json_type_array)) for(i=0;iuid = id; win->pid = json_int_by_name(obj,"pid",0); win->floating = json_bool_by_name(obj,"floating",FALSE); wintree_window_append(win); wintree_set_app_id(id,json_string_by_name(obj,"class")); wintree_set_title(id,json_string_by_name(obj,"title")); wintree_log(id); } else wintree_set_title(id,json_string_by_name(obj,"title")); if(hypr_ipc_workspace_id(obj)==GINT_TO_POINTER(-99)) win->state |= WS_MINIMIZED; else { win->state &= ~WS_MINIMIZED; win->workspace = hypr_ipc_workspace_id(obj); monitor = hypr_ipc_workspace_data(win->workspace,"monitor"); if(!g_list_find_custom(win->outputs,monitor,(GCompareFunc)g_strcmp0)) { g_list_free_full(win->outputs,g_free); win->outputs = g_list_prepend(win->outputs,monitor); } else g_free(monitor); } } static gboolean hypr_ipc_get_clients ( gpointer *uid ) { json_object *json, *ptr; gpointer id; gint i; if(!hypr_ipc_request(ipc_sockaddr,"j/clients",&json) || !json) return FALSE; if( json_object_is_type(json, json_type_array) ) for(i=0;istate |= WS_MINIMIZED; else win->state &= ~WS_MINIMIZED; } static GdkRectangle hypr_ipc_get_output_geom ( gpointer wsid ) { json_object *json, *iter; gint i, scale; gchar *monitor = NULL; GdkRectangle res; res.x = -1; res.y = -1; res.width = -1; res.height = -1; if(!hypr_ipc_request(ipc_sockaddr,"j/workspaces",&json) || !json) return res; if( json_object_is_type(json, json_type_array) ) for(i=0;iid); if(space->width<0) return 0; if(!hypr_ipc_request(ipc_sockaddr,"j/clients",&json) || !json) return 0; if( json_object_is_type(json, json_type_array) ) { *wins = g_malloc(sizeof(GdkRectangle)*json_object_array_length(json)); for(i=0;iid) { hypr_ipc_window_geom(iter,&((*wins)[n])); if(hypr_ipc_window_id(iter)==wintree_get_focus()) *focus = n; n++; } } } json_object_put(json); return n; } static void hypr_ipc_window_place ( gpointer wid ) { window_t *win; json_object *json, *iter; GdkRectangle space, *obs, window; gint i, nobs=0; win = wintree_from_id(wid); if(!win || !wintree_placer_check(win->pid)) return; space = hypr_ipc_get_output_geom(win->workspace); space.x=0; space.y=0; if(!hypr_ipc_request(ipc_sockaddr,"j/clients",&json) || !json || !json_object_is_type(json, json_type_array)) return; obs = g_malloc0(sizeof(GdkRectangle)*json_object_array_length(json)); for(i=0;iworkspace) { if(hypr_ipc_window_id(iter) == wid) hypr_ipc_window_geom(iter,&window); else hypr_ipc_window_geom(iter,&(obs[nobs++])); } } json_object_put(json); wintree_placer_calc(nobs,obs,space,&window); hypr_ipc_command("dispatch movewindowpixel exact %d %d,address:0x%lx", window.x,window.y,GPOINTER_TO_SIZE(wid)); g_free(obs); } static void hypr_ipc_pager_populate( void ) { json_object *json,*ptr, *iter; gint i, wid; workspace_t *ws; if(!hypr_ipc_request(ipc_sockaddr,"j/workspaces",&json) || !json) return; if(json_object_is_type(json, json_type_array)) for(i=0;iid = GINT_TO_POINTER(json_int_by_name(ptr,"id",-1)); ws->name = g_strdup(json_string_by_name(ptr,"name")); pager_workspace_new(ws); pager_invalidate_all(ws->id); g_free(ws->name); g_free(ws); } } json_object_put(json); if(!hypr_ipc_request(ipc_sockaddr,"j/monitors",&json) || !json) return; if(json_object_is_type(json, json_type_array)) for(i=0;ivisible = TRUE; pager_workspace_set_active(ws,json_string_by_name(iter,"name")); } } } } json_object_put(json); pager_update(); } static void hypr_ipc_track_focus ( void ) { json_object *json; if(!hypr_ipc_request(ipc_sockaddr,"j/activewindow",&json) || !json) return; wintree_set_focus(hypr_ipc_window_id(json)); json_object_put(json); } static void hypr_ipc_floating_set ( gchar *data ) { window_t *win; gchar *ptr; ptr = strchr(data,','); if(!ptr) return; win = wintree_from_id(hypr_ipc_parse_id(data)); if(!win) return; win->floating = *(ptr+1)=='1'; wintree_commit(win); } static void hypr_ipc_set_maximized ( gboolean state ) { window_t *win; win = wintree_from_id(wintree_get_focus()); if(!win) return; if(state) win->state |= WS_MAXIMIZED; else win->state &= ~WS_MAXIMIZED; } static void hypr_ipc_maximize ( gpointer id ) { window_t *win; gpointer focus; win = wintree_from_id(id); if(!win || win->state & WS_MAXIMIZED) return; focus = wintree_get_focus(); wintree_set_focus(id); hypr_ipc_command("dispatch fullscreen 0"); wintree_set_focus(focus); } static void hypr_ipc_unmaximize ( gpointer id ) { window_t *win; gpointer focus; win = wintree_from_id(id); if(!win || !(win->state & WS_MAXIMIZED)) return; focus = wintree_get_focus(); wintree_set_focus(id); hypr_ipc_command("dispatch fullscreen 0"); wintree_set_focus(focus); } static void hypr_ipc_minimize ( gpointer id ) { window_t *win; gpointer focus; json_object *json; win = wintree_from_id(id); if(!win || win->state & WS_MINIMIZED) return; focus = wintree_get_focus(); if(focus!=id) wintree_set_focus(id); if(wintree_get_disown()) win->workspace = NULL; else if(hypr_ipc_request(ipc_sockaddr,"j/activewindow",&json) && json) { win->workspace = hypr_ipc_workspace_id(json); json_object_put(json); } hypr_ipc_command("dispatch movetoworkspace special"); hypr_ipc_command("workspace %d",GPOINTER_TO_INT(win->workspace)); if(focus!=id) wintree_set_focus(focus); } static void hypr_ipc_unminimize ( gpointer id ) { window_t *win; json_object *json, *iter, *ptr; gint i, workspace=0; win = wintree_from_id(id); if(!win || !(win->state & WS_MINIMIZED)) return; if(win->workspace) workspace = GPOINTER_TO_INT(win->workspace); else { if(!hypr_ipc_request(ipc_sockaddr,"j/monitors",&json) || !json || !json_object_is_type(json, json_type_array)) return; for(i=0;istate & WS_MINIMIZED) hypr_ipc_unminimize(id); hypr_ipc_command("dispatch focuswindow address:0x%lx",GPOINTER_TO_SIZE(id)); } static void hypr_ipc_set_workspace ( workspace_t *ws ) { gchar *name; name = hypr_ipc_workspace_data(ws->id,"name"); if(name) hypr_ipc_command("dispatch workspace name:%s",name); else hypr_ipc_command("dispatch workspace name:%s",ws->name); g_free(name); } static struct wintree_api hypr_wintree_api = { .minimize = hypr_ipc_minimize, .unminimize = hypr_ipc_unminimize, .maximize = hypr_ipc_maximize, .unmaximize = hypr_ipc_unmaximize, .close = hypr_ipc_close, .focus = hypr_ipc_focus }; static struct pager_api hypr_pager_api = { .set_workspace = hypr_ipc_set_workspace, .get_geom = hypr_ipc_get_geom }; static gboolean hypr_ipc_event ( GIOChannel *chan, GIOCondition cond, gpointer data) { gchar *event,*ptr; (void)g_io_channel_read_line(chan,&event,NULL,NULL,NULL); while(event) { if((ptr=strchr(event,'\n'))) *ptr=0; g_debug("hypr event: %s",event); if(!strncmp(event,"activewindow>>",14)) hypr_ipc_track_focus(); else if(!strncmp(event,"openwindow>>",12)) { hypr_ipc_get_clients(hypr_ipc_parse_id(event+12)); hypr_ipc_window_place(hypr_ipc_parse_id(event+12)); } else if(!strncmp(event,"closewindow>>",13)) wintree_window_delete(hypr_ipc_parse_id(event+13)); else if(!strncmp(event,"fullscreen>>",12)) hypr_ipc_set_maximized(g_ascii_digit_value(*(event+12))); else if(!strncmp(event,"movewindow>>",12)) hypr_ipc_track_minimized(event+12); else if(!strncmp(event,"workspace>>",11)) hypr_ipc_pager_populate(); else if(!strncmp(event,"focusedmon>>",12)) hypr_ipc_pager_populate(); else if(!strncmp(event,"createworkspace>>",17)) hypr_ipc_pager_populate(); else if(!strncmp(event,"changefloatingmode>>",20)) hypr_ipc_floating_set(event+20); else if(!strncmp(event,"destroyworkspace>>",18)) pager_workspace_delete(pager_workspace_id_from_name(event+18)); g_free(event); (void)g_io_channel_read_line(chan,&event,NULL,NULL,NULL); } pager_update(); return TRUE; } void hypr_ipc_init ( void ) { gchar *sockaddr; gint sock; if(ipc_get()) return; ipc_sockaddr = g_build_filename("/tmp/hypr", g_getenv("HYPRLAND_INSTANCE_SIGNATURE"),".socket.sock",NULL); if(!hypr_ipc_get_clients(NULL)) { g_free(ipc_sockaddr); return; } ipc_set(IPC_HYPR); hypr_ipc_track_focus(); sockaddr = g_build_filename("/tmp","hypr", g_getenv("HYPRLAND_INSTANCE_SIGNATURE"),".socket2.sock",NULL); sock = socket_connect(sockaddr,10); if(sock!=-1) g_io_add_watch(g_io_channel_unix_new(sock),G_IO_IN,hypr_ipc_event,NULL); g_free(sockaddr); hypr_ipc_pager_populate(); wintree_api_register(&hypr_wintree_api); pager_api_register(&hypr_pager_api); } sfwbar-1.0~beta13/src/image.c000066400000000000000000000027431450657570000160700ustar00rootroot00000000000000/* This entire file is licensed under GNU General Public License v3.0 * * Copyright 2022- sfwbar maintainers */ #include "sfwbar.h" #include "basewidget.h" #include "image.h" #include "scaleimage.h" G_DEFINE_TYPE_WITH_CODE (Image, image, BASE_WIDGET_TYPE, G_ADD_PRIVATE (Image)); static void image_update_value ( GtkWidget *self ) { ImagePrivate *priv; g_return_if_fail(IS_IMAGE(self)); priv = image_get_instance_private(IMAGE(self)); scale_image_set_image(GTK_WIDGET(priv->image), base_widget_get_value(self),NULL); } static GtkWidget *image_get_child ( GtkWidget *self ) { ImagePrivate *priv; g_return_val_if_fail(IS_IMAGE(self),NULL); priv = image_get_instance_private(IMAGE(self)); return priv->image; } static GtkWidget *image_mirror ( GtkWidget *src ) { GtkWidget *self; g_return_val_if_fail(IS_IMAGE(src),NULL); self = image_new(); base_widget_copy_properties(self,src); return self; } static void image_class_init ( ImageClass *kclass ) { BASE_WIDGET_CLASS(kclass)->update_value = image_update_value; BASE_WIDGET_CLASS(kclass)->get_child = image_get_child; BASE_WIDGET_CLASS(kclass)->mirror = image_mirror; } static void image_init ( Image *self ) { } GtkWidget *image_new ( void ) { GtkWidget *self; ImagePrivate *priv; self = GTK_WIDGET(g_object_new(image_get_type(), NULL)); priv = image_get_instance_private(IMAGE(self)); priv->image = scale_image_new(); gtk_container_add(GTK_CONTAINER(self),priv->image); return self; } sfwbar-1.0~beta13/src/image.h000066400000000000000000000013741450657570000160740ustar00rootroot00000000000000#ifndef __IMAGE_H__ #define __IMAGE_H__ #include "basewidget.h" #define IMAGE_TYPE (image_get_type()) #define IMAGE(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), IMAGE_TYPE, Image)) #define IMAGE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), IMAGE_TYPE, ImageClass)) #define IS_IMAGE(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), IMAGE_TYPE)) #define IS_IMAGECLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), IMAGE_TYPE)) typedef struct _Image Image; typedef struct _ImageClass ImageClass; struct _Image { BaseWidget item; }; struct _ImageClass { BaseWidgetClass parent_class; }; typedef struct _ImagePrivate ImagePrivate; struct _ImagePrivate { GtkWidget *image; }; GType image_get_type ( void ); GtkWidget *image_new(); #endif sfwbar-1.0~beta13/src/jpath.c000066400000000000000000000124731450657570000161150ustar00rootroot00000000000000/* This entire file is licensed under GNU General Public License v3.0 * * Copyright 2022- sfwbar maintainers */ #include #include "sfwbar.h" gboolean jpath_filter_test ( GScanner *scanner, gint idx, gchar *key, gboolean eq, struct json_object *obj, GTokenType type, GTokenValue val ) { struct json_object *tmp; switch(type) { case ']': return TRUE; break; case G_TOKEN_INT: if(idx==val.v_int) return TRUE; break; case G_TOKEN_STRING: json_object_object_get_ex(obj,key,&tmp); if(tmp && !eq) return TRUE; if(tmp && eq && scanner->token == G_TOKEN_STRING) if(!g_ascii_strcasecmp(val.v_string,json_object_get_string(tmp))) return TRUE; if(tmp && eq && scanner->token == G_TOKEN_INT) if(val.v_int == json_object_get_int64(tmp)) return TRUE; if(tmp && eq && scanner->token == G_TOKEN_FLOAT) if(val.v_float == json_object_get_double(tmp)) return TRUE; break; default: break; } return FALSE; } struct json_object *jpath_filter (GScanner *scanner, struct json_object *obj ) { struct json_object *next, *iter, *jiter; gint i,j; gchar *key=NULL; GTokenType type; GTokenValue val; gboolean eq = FALSE; next = json_object_new_array(); type = g_scanner_get_next_token(scanner); switch(type) { case G_TOKEN_STRING: key = g_strdup(scanner->value.v_string); if(g_scanner_peek_next_token(scanner)=='=') { eq = TRUE; g_scanner_get_next_token(scanner); scanner->config->scan_float = 1; g_scanner_get_next_token(scanner); val = scanner->value; scanner->config->scan_float = 0; } else eq = FALSE; break; case ']': break; case G_TOKEN_INT: val = scanner->value; break; default: return next; break; } for(i=0;ivalue.v_string,&tmp); if(tmp) json_object_array_add(next,tmp); } else { json_object_object_get_ex(json_object_array_get_idx(obj,i), scanner->value.v_string,&tmp); if(tmp) json_object_array_add(next,tmp); } } return next; } struct json_object *jpath_index ( GScanner *scanner, struct json_object *obj ) { struct json_object *next, *iter; gint i; next = json_object_new_array(); for(i=0;ivalue.v_int)); } return next; } struct json_object *jpath_parse ( gchar *path, struct json_object *obj ) { GScanner *scanner; struct json_object *cur,*next; gint i, sep; if(!path || !obj) return NULL; scanner = g_scanner_new(NULL); scanner->config->scan_octal = 0; scanner->config->symbol_2_token = 1; scanner->config->char_2_token = 0; scanner->config->scan_float = 0; scanner->config->case_sensitive = 0; scanner->config->numbers_2_int = 1; scanner->config->identifier_2_string = 1; scanner->input_name = path; scanner->user_data = path; g_scanner_input_text(scanner, path, strlen(path)); if(g_scanner_get_next_token(scanner)!=G_TOKEN_CHAR) return NULL; sep = scanner->value.v_char; scanner->config->char_2_token = 1; json_object_get(obj); if(json_object_is_type(obj,json_type_array)) cur = obj; else { cur = json_object_new_array(); json_object_array_add(cur,obj); } do { switch((gint)g_scanner_get_next_token(scanner)) { case '[': next = jpath_filter(scanner,cur); break; case G_TOKEN_STRING: next = jpath_key(scanner,cur); break; case G_TOKEN_INT: next = jpath_index(scanner,cur); break; default: g_scanner_error(scanner,"invalid token in json path %d %d",scanner->token,G_TOKEN_ERROR); next = NULL; break; } if(next) { for(i=0;ilabel),base_widget_get_value(self)); } static GtkWidget *label_get_child ( GtkWidget *self ) { LabelPrivate *priv; g_return_val_if_fail(IS_LABEL(self),NULL); priv = label_get_instance_private(LABEL(self)); return priv->label; } static GtkWidget *label_mirror ( GtkWidget *src ) { GtkWidget *self; g_return_val_if_fail(IS_LABEL(src),NULL); self = label_new(); base_widget_copy_properties(self,src); return self; } static void label_class_init ( LabelClass *kclass ) { BASE_WIDGET_CLASS(kclass)->update_value = label_update_value; BASE_WIDGET_CLASS(kclass)->get_child = label_get_child; BASE_WIDGET_CLASS(kclass)->mirror = label_mirror; } static void label_init ( Label *self ) { } GtkWidget *label_new ( void ) { GtkWidget *self; LabelPrivate *priv; self = GTK_WIDGET(g_object_new(label_get_type(), NULL)); priv = label_get_instance_private(LABEL(self)); priv->label = gtk_label_new(""); gtk_label_set_ellipsize(GTK_LABEL(priv->label),PANGO_ELLIPSIZE_END); gtk_container_add(GTK_CONTAINER(self),priv->label); return self; } sfwbar-1.0~beta13/src/label.h000066400000000000000000000013741450657570000160710ustar00rootroot00000000000000#ifndef __LABEL_H__ #define __LABEL_H__ #include "basewidget.h" #define LABEL_TYPE (label_get_type()) #define LABEL(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), LABEL_TYPE, Label)) #define LABEL_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), LABEL_TYPE, LabelClass)) #define IS_LABEL(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), LABEL_TYPE)) #define IS_LABELCLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), LABEL_TYPE)) typedef struct _Label Label; typedef struct _LabelClass LabelClass; struct _Label { BaseWidget item; }; struct _LabelClass { BaseWidgetClass parent_class; }; typedef struct _LabelPrivate LabelPrivate; struct _LabelPrivate { GtkWidget *label; }; GType label_get_type ( void ); GtkWidget *label_new(); #endif sfwbar-1.0~beta13/src/menu.c000066400000000000000000000103061450657570000157440ustar00rootroot00000000000000/* This entire file is licensed under GNU General Public License v3.0 * * Copyright 2021- sfwbar maintainers */ #include "sfwbar.h" #include "menu.h" #include "window.h" #include "taskbargroup.h" #include "scaleimage.h" #include "popup.h" static GHashTable *menus; GtkWidget *menu_from_name ( gchar *name ) { if(!menus || !name) return NULL; return g_hash_table_lookup(menus, name); } void menu_remove ( gchar *name ) { GtkWidget *menu; GList *items, *iter; if(!menus || !name) return; menu = menu_from_name(name); if(!menu) return; items = gtk_container_get_children(GTK_CONTAINER(menu)); for(iter=items;iter;iter=g_list_next(iter)) if(gtk_menu_item_get_submenu(iter->data)) gtk_menu_item_set_submenu(iter->data,NULL); g_list_free(items); g_hash_table_remove(menus,name); } void menu_clamp_size ( GtkMenu *menu ) { GdkDisplay *display; GdkMonitor *monitor; GdkWindow *gdk_win; GtkWindow *toplevel; GdkRectangle workarea; gint w,h; toplevel = GTK_WINDOW(gtk_widget_get_ancestor(GTK_WIDGET(menu),GTK_TYPE_WINDOW)); gdk_win = gtk_widget_get_window(GTK_WIDGET(toplevel)); w = gdk_window_get_width(gdk_win); h = gdk_window_get_height(gdk_win); display = gdk_window_get_display(gdk_win); monitor = gdk_display_get_monitor_at_window(display,gdk_win); gdk_monitor_get_workarea(monitor,&workarea); gdk_window_resize(gdk_win,MIN(w,workarea.width),MIN(h,workarea.height)); } GtkWidget *menu_new ( gchar *name ) { GtkWidget *menu; if(name) { menu = menu_from_name(name); if(menu) return menu; } menu = gtk_menu_new(); g_signal_connect(menu,"popped-up",G_CALLBACK(menu_clamp_size),NULL); gtk_menu_set_reserve_toggle_size(GTK_MENU(menu), FALSE); if(name) { g_object_ref_sink(G_OBJECT(menu)); if(!menus) menus = g_hash_table_new_full((GHashFunc)str_nhash, (GEqualFunc)str_nequal, g_free,g_object_unref); g_hash_table_insert(menus, g_strdup(name), menu); } return menu; } void menu_popup( GtkWidget *widget, GtkWidget *menu, GdkEvent *event, gpointer wid, guint16 *state ) { GdkGravity wanchor, manchor; GtkWidget *window; if(!menu || !widget) return; if(state) g_object_set_data( G_OBJECT(menu), "state", GUINT_TO_POINTER(*state) ); g_object_set_data( G_OBJECT(menu), "wid", wid ); g_object_set_data( G_OBJECT(menu), "caller", widget ); window = gtk_widget_get_ancestor(widget,GTK_TYPE_WINDOW); if(gtk_window_get_window_type(GTK_WINDOW(window)) == GTK_WINDOW_POPUP) taskbar_group_pop_child(window, menu); widget = GTK_IS_BIN(widget)?gtk_bin_get_child(GTK_BIN(widget)):widget; popup_get_gravity(widget,&wanchor,&manchor); gtk_widget_show_all(menu); gtk_menu_popup_at_widget(GTK_MENU(menu),widget,wanchor,manchor,event); window_ref(window,menu); } gboolean menu_action_cb ( GtkWidget *w ,action_t *action ) { GtkWidget *parent = gtk_widget_get_ancestor(w,GTK_TYPE_MENU); gpointer wid; guint16 state; GtkWidget *widget; if(parent) { wid = g_object_get_data ( G_OBJECT(parent), "wid" ); state = GPOINTER_TO_UINT(g_object_get_data ( G_OBJECT(parent), "state" )); widget = g_object_get_data ( G_OBJECT(parent),"caller" ); } else { wid = NULL; state = 0; widget = NULL; } if(!wid) wid = wintree_get_focus(); action_exec(widget,action,NULL,wintree_from_id(wid),&state); return TRUE; } GtkWidget *menu_item_new ( gchar *label, action_t *action ) { GtkWidget *item,*box,*wlabel,*img; gchar *text, *icon; icon = strchr(label,'%'); if(icon) text = g_strndup(label,icon-label); else text = g_strdup(label); item = gtk_menu_item_new(); gtk_widget_set_name(item,"menu_item"); box = gtk_grid_new(); if(icon) { img = scale_image_new(); scale_image_set_image(img,icon+1,NULL); if(img) gtk_grid_attach(GTK_GRID(box),img,1,1,1,1); } if(text) { wlabel = gtk_label_new_with_mnemonic(text); gtk_grid_attach(GTK_GRID(box),wlabel,2,1,1,1); g_free(text); } gtk_container_add(GTK_CONTAINER(item),box); if(action) { g_signal_connect(G_OBJECT(item),"activate", G_CALLBACK(menu_action_cb),action); g_object_weak_ref(G_OBJECT(item),(GWeakNotify)action_free,action); } return item; } sfwbar-1.0~beta13/src/menu.h000066400000000000000000000005271450657570000157550ustar00rootroot00000000000000#ifndef __MENU_H__ #define __MENU_H__ #include #include "action.h" GtkWidget *menu_from_name ( gchar *name ); GtkWidget *menu_new ( gchar *name ); void menu_remove ( gchar *name ); void menu_popup ( GtkWidget *, GtkWidget *, GdkEvent *, gpointer, guint16 * ); GtkWidget *menu_item_new ( gchar *label, action_t *action ); #endif sfwbar-1.0~beta13/src/misc.c000066400000000000000000000134071450657570000157400ustar00rootroot00000000000000/* This entire file is licensed under GNU General Public License v3.0 * * Copyright 2020- sfwbar maintainers */ #include #include #include #include #include #include #include extern gchar *confname; gint socket_connect ( const gchar *sockaddr, gint to ) { gint sock; struct sockaddr_un addr; struct timeval timeout = {.tv_sec = to/1000, .tv_usec = to%1000}; sock = socket(AF_UNIX,SOCK_STREAM,0); if (sock == -1) return -1; addr.sun_family = AF_UNIX; strncpy(addr.sun_path, sockaddr, sizeof(addr.sun_path) - 1); if(connect(sock,(struct sockaddr *)&addr,sizeof(struct sockaddr_un)) != -1 ) if(setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, &timeout, sizeof(timeout)) != -1) return sock; close(sock); return -1; } json_object *recv_json ( gint sock, gint32 len ) { static gchar *buf; const gint bufsize = 1024; json_tokener *tok; json_object *json = NULL; gint rlen; if(!buf) buf = g_malloc(bufsize); tok = json_tokener_new(); while(len!=0 && (rlen = recv(sock,buf,len<0?bufsize:MIN(len,bufsize),0))>0 ) { json = json_tokener_parse_ex(tok,buf,rlen); if(len>0) len-=rlen; } json_tokener_free(tok); return json; } void list_remove_link ( GList **list, void *child ) { *list = g_list_delete_link(*list,g_list_find(*list,child)); } gboolean file_test_read ( gchar *filename ) { if( !g_file_test(filename, G_FILE_TEST_EXISTS) ) return FALSE; if( access(filename, R_OK) == -1 ) return FALSE; return TRUE; } /* get xdg config file name, first try user xdg config directory, * if file doesn't exist, try system xdg data dirs */ gchar *get_xdg_config_file ( gchar *fname, gchar *extra ) { gchar *full, *dir; const gchar * const *xdg_data; gint i; if(fname && *fname == '/') { if(file_test_read(fname)) return g_strdup(fname); else return NULL; } full = g_build_filename( ".", fname, NULL ); if( file_test_read(full) ) return full; else g_free(full); if(confname!=NULL) { dir = g_path_get_dirname(confname); full = g_build_filename ( dir, fname, NULL ); g_free(dir); if( file_test_read(full) ) return full; g_free(full); } full = g_build_filename ( g_get_user_config_dir(), "sfwbar", fname, NULL ); if( file_test_read(full) ) return full; g_free(full); xdg_data = g_get_system_data_dirs(); for(i=0;xdg_data[i];i++) { full = g_build_filename ( xdg_data[i], "sfwbar", fname, NULL ); if( file_test_read(full) ) return full; g_free(full); } if(!extra) return NULL; full = g_build_filename ( extra, fname, NULL ); if( file_test_read(full) ) return full; g_free(full); return NULL; } /* get string value from an object within current object */ const gchar *json_string_by_name ( struct json_object *obj, gchar *name ) { struct json_object *ptr; if(json_object_object_get_ex(obj,name,&ptr)) return json_object_get_string(ptr); return NULL; } /* get int value from an object within current object */ gint64 json_int_by_name ( struct json_object *obj, gchar *name, gint64 defval) { struct json_object *ptr; if(json_object_object_get_ex(obj,name,&ptr)) return json_object_get_int64(ptr); return defval; } /* get bool value from an object within current object */ gboolean json_bool_by_name ( struct json_object *obj, gchar *name, gboolean defval) { struct json_object *ptr; if(json_object_object_get_ex(obj,name,&ptr) && ptr) return json_object_get_boolean(ptr); return defval; } /* get double value from an object within current object */ gdouble json_double_by_name ( struct json_object *obj, gchar *name, gdouble defval) { struct json_object *ptr; if(json_object_object_get_ex(obj,name,&ptr)) return json_object_get_double(ptr); return defval; } gboolean pattern_match ( gchar **dict, gchar *string ) { gint i; if(!dict) return FALSE; for(i=0;dict[i];i++) if(g_pattern_match_simple(dict[i],string)) return TRUE; return FALSE; } gboolean regex_match_list ( GList *dict, gchar *string ) { GList *iter; for(iter=dict;iter;iter=g_list_next(iter)) if(g_regex_match(iter->data, string, 0, NULL)) return TRUE; return FALSE; } /* compute md5 checksum for a file */ int md5_file( gchar *path, guchar output[16] ) { FILE *f; gssize n; GChecksum *ctx; guchar buf[1024]; if( ( f = fopen( path, "rb" ) ) == NULL ) return( 1 ); ctx = g_checksum_new(G_CHECKSUM_MD5); while( ( n = fread( buf, 1, sizeof( buf )-1, f ) ) > 0 ) { buf[n]='\0'; g_checksum_update( ctx, buf, n ); } n=16; g_checksum_get_digest( ctx, output, (gsize *)&n ); g_checksum_free( ctx ); fclose( f ); return( 0 ); } void str_assign ( gchar **dest, gchar *source ) { g_free(*dest); if(source==NULL) *dest = NULL; else *dest = g_strdup(source); } guint str_nhash ( gchar *str ) { guint ret = 5381; guint i=0; while(str[i]) ret += g_ascii_toupper(str[i++]); return ret; } gboolean str_nequal ( gchar *str1, gchar *str2 ) { return (!g_ascii_strcasecmp(str1,str2)); } gchar *str_replace ( gchar *str, gchar *old, gchar *new ) { gssize olen, nlen; gint n; gchar *ptr, *pptr, *dptr, *dest; if(!str || !old || !new) return g_strdup(str); olen = strlen(old); nlen = strlen(new); n=0; ptr = strstr(str,old); while(ptr) { n++; ptr = strstr(ptr+olen,old); } if(!n) return g_strdup(str); dest = g_malloc(strlen(str)+n*(nlen-olen)+1); pptr = str; ptr = strstr(str,old); dptr = dest; while(ptr) { strncpy(dptr,pptr,ptr-pptr); dptr += ptr - pptr; strcpy(dptr,new); dptr += nlen; pptr = ptr + olen; ptr = strstr(pptr,old); } strcpy(dptr,pptr); return dest; } sfwbar-1.0~beta13/src/module.c000066400000000000000000000127331450657570000162730ustar00rootroot00000000000000/* This entire file is licensed under GNU General Public License v3.0 * * Copyright 2022- sfwbar maintainers */ #include "expr.h" #include #include #include "module.h" #include "config.h" #include "basewidget.h" #include "../meson.h" GList *module_list; GHashTable *expr_handlers; GData *act_handlers; GList *invalidators; static ModuleApiV1 api_v1 = { .emit_trigger = base_widget_emit_trigger, .config_string = config_string, }; void module_expr_funcs_add ( ModuleExpressionHandlerV1 **ehandler,gchar *name ) { gint i; for(i=0;ehandler[i];i++) if(ehandler[i]->function && ehandler[i]->name) { if(!expr_handlers) expr_handlers = g_hash_table_new((GHashFunc)str_nhash, (GEqualFunc)str_nequal); g_debug("module: register expr function '%s'",ehandler[i]->name); if(g_hash_table_lookup(expr_handlers,ehandler[i]->name)) g_message("Duplicate module expr function: %s in module %s", ehandler[i]->name,name); else { g_hash_table_insert(expr_handlers,ehandler[i]->name,ehandler[i]); expr_dep_trigger(ehandler[i]->name); } } } void module_actions_add ( ModuleActionHandlerV1 **ahandler, gchar *name ) { gint i; gchar *lname; for(i=0;ahandler[i];i++) if(ahandler[i]->function && ahandler[i]->name) { lname = g_ascii_strdown(ahandler[i]->name,-1); g_debug("module: register action '%s'",ahandler[i]->name); if(g_datalist_get_data(&act_handlers,lname)) g_message("Duplicate module action: %s in module %s", ahandler[i]->name,name); else g_datalist_set_data(&act_handlers,lname,ahandler[i]); g_free(lname); } } gboolean module_load ( gchar *name ) { GModule *module; ModuleExpressionHandlerV1 **ehandler; ModuleActionHandlerV1 **ahandler; ModuleInvalidator invalidator; ModuleInitializer init; gint64 *sig; guint16 *ver; gchar *fname, *path; if(!name) return FALSE; g_debug("module: %s", name); if(g_list_find_custom(module_list,name,(GCompareFunc)g_strcmp0)) return FALSE; fname = g_strconcat(name,".so",NULL); path = get_xdg_config_file(fname,MODULE_DIR); g_free(fname); g_debug("module: %s --> %s",name,path); module = g_module_open(path, G_MODULE_BIND_LOCAL); g_free(path); if(!module) { g_debug("module: failed to load %s",name); return FALSE; } if(!g_module_symbol(module,"sfwbar_module_signature",(void **)&sig) || !sig || *sig != 0x73f4d956a1 ) { g_debug("module: signature check failed for %s",name); return FALSE; } if(!g_module_symbol(module,"sfwbar_module_version",(void **)&ver) || !ver || *ver != 1 ) { g_debug("module: invalid version for %s",name); return FALSE; } module_list = g_list_prepend(module_list,g_strdup(name)); if(g_module_symbol(module,"sfwbar_module_init",(void **)&init) && init) { g_debug("module: calling init function for %s",name); api_v1.gmc = g_main_context_get_thread_default(); init(&api_v1); } if(g_module_symbol(module,"sfwbar_module_invalidate",(void **)&invalidator)) invalidators = g_list_prepend(invalidators,invalidator); if(g_module_symbol(module,"sfwbar_expression_handlers",(void **)&ehandler)) module_expr_funcs_add(ehandler, name); if(g_module_symbol(module,"sfwbar_action_handlers",(void **)&ahandler)) module_actions_add(ahandler, name); return TRUE; } void module_invalidate_all ( void ) { GList *iter; for(iter=invalidators;iter;iter=g_list_next(iter)) if(iter->data) ((ModuleInvalidator)(iter->data))(); } ModuleActionHandlerV1 *module_action_get ( GQuark quark ) { return g_datalist_id_get_data(&act_handlers, quark); } void module_action_exec ( GQuark quark, gchar *param, gchar *addr, void *widget, void *ev, void *win, void *state) { ModuleActionHandlerV1 *handler; g_debug("module: checking action `%s`",g_quark_to_string(quark)); handler = module_action_get(quark); if(!handler) return; g_debug("module: calling action `%s`",g_quark_to_string(quark)); handler->function(param, addr, widget,ev,win,state); } gboolean module_is_function ( gchar *identifier ) { if(expr_handlers && g_hash_table_lookup(expr_handlers,identifier)) return TRUE; return FALSE; } gboolean module_check_flag ( gchar *identifier, gint flag ) { ModuleExpressionHandlerV1 *handler; if(!expr_handlers) return FALSE; handler = g_hash_table_lookup(expr_handlers,identifier); if(!handler) return FALSE; return !!(handler->flags & flag); } gchar *module_get_string ( GScanner *scanner ) { ModuleExpressionHandlerV1 *handler; void **params; ExprCache *expr; gchar *result; gint i; E_STATE(scanner)->type = EXPR_VARIANT; if(!expr_handlers) return g_strdup(""); handler = g_hash_table_lookup(expr_handlers,scanner->value.v_identifier); if(!handler) return g_strdup(""); g_debug("module: calling function `%s`",handler->name); params = expr_module_parameters(scanner,handler->parameters,handler->name); for(expr=E_STATE(scanner)->expr;!expr->widget&&expr->parent;expr=expr->parent); result = handler->function(params, expr->widget, expr->event); if(params) for(i=0;iparameters);i++) g_free(params[i]); g_free(params); if(handler->flags & MODULE_EXPR_NUMERIC) E_STATE(scanner)->type = EXPR_NUMERIC; else E_STATE(scanner)->type = EXPR_STRING; if(!(handler->flags & MODULE_EXPR_DETERMINISTIC)) E_STATE(scanner)->expr->vstate = TRUE; return result; } sfwbar-1.0~beta13/src/module.h000066400000000000000000000035771450657570000163060ustar00rootroot00000000000000 #ifndef __MODULE_H__ #define __MODULE_H__ #include typedef struct { GMainContext *gmc; gboolean (*emit_trigger)(gchar *); void (*config_string)(gchar *); } ModuleApiV1; typedef gpointer (*ModuleExpressionFunc)(gpointer *, gpointer, gpointer); typedef void (*ModuleActionFunc)(gchar *, gchar *, void *, void *, void *, void *); typedef void (*ModuleInvalidator)( void ); typedef void (*ModuleInitializer)( ModuleApiV1 * ); typedef gboolean (*ModuleEmitTrigger) ( gchar * ); typedef void (*ModuleInitTrigger) (GMainContext *, ModuleEmitTrigger ); typedef struct { gchar *name; ModuleExpressionFunc function; gchar *parameters; gint flags; } ModuleExpressionHandlerV1; typedef struct { gchar *name; gint flags; ModuleActionFunc function; } ModuleActionHandlerV1; enum ModuleFlags { MODULE_EXPR_NUMERIC = 1, MODULE_EXPR_DETERMINISTIC = 2 }; enum ModuleActionFlags { MODULE_ACT_WIDGET_ADDRESS = 1, MODULE_ACT_CMD_BY_DEF = 2, MODULE_ACT_ADDRESS_ONLY = 4 }; #define MODULE_TRIGGER_EMIT(x) \ if(sfwbar_module_api && sfwbar_module_api->emit_trigger) \ g_main_context_invoke(sfwbar_module_api->gmc, \ (GSourceFunc)sfwbar_module_api->emit_trigger,x); #define MODULE_CONFIG_STRING(x) \ if(sfwbar_module_api && sfwbar->module_api->config_string) \ sfwbar_module_api->config_string(x) gboolean module_load ( gchar *name ); void module_invalidate_all ( void ); gboolean module_is_function ( gchar *identifier ); gboolean module_check_flag ( gchar *identifier, gint flag ); gchar *module_get_string ( GScanner *scanner ); ModuleActionHandlerV1 *module_action_get ( GQuark quark ); void module_action_exec ( GQuark quark, gchar *param, gchar *addr, void *, void *, void *, void * ); void module_actions_add ( ModuleActionHandlerV1 **ahandler, gchar *name ); void module_expr_funcs_add ( ModuleExpressionHandlerV1 **ehandler,gchar *name); #endif sfwbar-1.0~beta13/src/mpd.c000066400000000000000000000057231450657570000155670ustar00rootroot00000000000000/* This entire file is licensed under GNU General Public License v3.0 * * Copyright 2022- sfwbar maintainers */ #include #include #include #include "sfwbar.h" #include "basewidget.h" #include "client.h" typedef struct _MpdState { gchar *path; gboolean idle; GQueue *commands; } MpdState; #define MPD_STATE(x) ((MpdState *)(x)) gboolean client_mpd_connect ( Client *client ) { gchar *host, *port; const gchar *dir; g_free(client->file->fname); if(MPD_STATE(client->data)->path && *(MPD_STATE(client->data)->path) ) client->file->fname = g_strdup(MPD_STATE(client->data)->path); else { dir = g_get_user_runtime_dir(); client->file->fname = g_build_filename(dir?dir:"/run","/mpd/socket",NULL); if( !g_file_test(client->file->fname, G_FILE_TEST_EXISTS) && g_strcmp0(client->file->fname,"/run/mp/socket") ) { g_free(client->file->fname); client->file->fname = g_strdup("/run/mpd/socket"); } if( !g_file_test(client->file->fname, G_FILE_TEST_EXISTS) ) { host = g_strdup(g_getenv("MPD_HOST")); port = g_strdup(g_getenv("MPD_PORT")); client->file->fname = g_strconcat(host?host:"localhost",":", port?port:"6600",NULL); } } return client_socket_connect(client); } GIOStatus client_mpd_respond ( Client *client ) { GIOStatus s; gchar *str; if(!client || !client->out || !client->data) return G_IO_ERROR; if(!g_queue_is_empty(MPD_STATE(client->data)->commands)) { str = g_queue_pop_head(MPD_STATE(client->data)->commands); s = g_io_channel_write_chars(client->out,str,-1,NULL,NULL); g_free(str); } else { MPD_STATE(client->data)->idle = !MPD_STATE(client->data)->idle; s = g_io_channel_write_chars(client->out, MPD_STATE(client->data)->idle? "status\ncurrentsong\n":"idle player options\n", -1,NULL,NULL); } g_io_channel_flush(client->out,NULL); return s; } void client_mpd ( ScanFile *file ) { Client *client; if( !file || !file->fname ) return; client = g_malloc0(sizeof(Client)); client->file = file; client->data = g_malloc0(sizeof(MpdState)); client->connect = client_mpd_connect; client->respond = client_mpd_respond; MPD_STATE(client->data)->commands = g_queue_new(); MPD_STATE(client->data)->path = g_strdup(file->fname); g_free(file->trigger); file->trigger = g_strdup("mpd"); file->source = SO_CLIENT; file->client = client; client_attach(client); } void client_mpd_command ( gchar *command ) { ScanFile *file; Client *client; if(!command) return; file = scanner_file_get("mpd"); if(!file) return; client = file->client; if(!client || !client->out || !client->data) return; g_queue_push_tail(MPD_STATE(client->data)->commands, g_strconcat(command,"\n",NULL)); (void)g_io_channel_write_chars(client->out,"noidle\n",-1,NULL,NULL); g_io_channel_flush(client->out,NULL); MPD_STATE(client->data)->idle = FALSE; } sfwbar-1.0~beta13/src/pager.c000066400000000000000000000177651450657570000161160ustar00rootroot00000000000000/* This entire file is licensed under GNU General Public License v3.0 * * Copyright 2020- sfwbar maintainers */ #include #include "sfwbar.h" #include "pager.h" #include "pageritem.h" #include "taskbar.h" G_DEFINE_TYPE_WITH_CODE (Pager, pager, BASE_WIDGET_TYPE, G_ADD_PRIVATE (Pager)); #define PAGER_PIN_ID (GINT_TO_POINTER(-1)) static struct pager_api api; static GList *pagers; static GList *global_pins; static workspace_t *focus; static GList *workspaces; static GHashTable *actives; void pager_api_register ( struct pager_api *new ) { api = *new; } void pager_set_workspace ( workspace_t *ws ) { if(api.set_workspace) api.set_workspace(ws); } guint pager_get_geom ( workspace_t *ws, GdkRectangle **wins, GdkRectangle *spc, gint *focus) { if(api.get_geom) return api.get_geom(ws,wins,spc,focus); return 0; } static GtkWidget *pager_get_child ( GtkWidget *self ) { PagerPrivate *priv; g_return_val_if_fail(IS_PAGER(self),NULL); priv = pager_get_instance_private(PAGER(self)); return priv->pager; } static GtkWidget *pager_mirror ( GtkWidget *src ) { GtkWidget *self; PagerPrivate *spriv, *dpriv; g_return_val_if_fail(IS_PAGER(src),NULL); self = pager_new(); dpriv = pager_get_instance_private(PAGER(self)); spriv = pager_get_instance_private(PAGER(src)); g_object_set_data(G_OBJECT(dpriv->pager),"preview", g_object_get_data(G_OBJECT(spriv->pager),"preview")); g_object_set_data(G_OBJECT(dpriv->pager),"sort_numeric", g_object_get_data(G_OBJECT(spriv->pager),"sort_numeric")); g_object_set_data(G_OBJECT(dpriv->pager),"pins",g_list_copy_deep( g_object_get_data(G_OBJECT(spriv->pager),"pins"), (GCopyFunc)g_strdup,NULL)); flow_grid_copy_properties(self,src); base_widget_copy_properties(self,src); pager_populate(); return self; } static void pager_destroy ( GtkWidget *self ) { PagerPrivate *priv; priv = pager_get_instance_private(PAGER(self)); pagers = g_list_remove(pagers,self); g_list_free_full(g_object_get_data(G_OBJECT(priv->pager),"pins"),g_free); g_object_set_data(G_OBJECT(priv->pager),"pins",NULL); GTK_WIDGET_CLASS(pager_parent_class)->destroy(self); } static void pager_class_init ( PagerClass *kclass ) { BASE_WIDGET_CLASS(kclass)->get_child = pager_get_child; BASE_WIDGET_CLASS(kclass)->mirror = pager_mirror; GTK_WIDGET_CLASS(kclass)->destroy = pager_destroy; BASE_WIDGET_CLASS(kclass)->action_exec = NULL; } static void pager_init ( Pager *self ) { } GtkWidget *pager_new ( void ) { GtkWidget *self; PagerPrivate *priv; self = GTK_WIDGET(g_object_new(pager_get_type(), NULL)); priv = pager_get_instance_private(PAGER(self)); priv->pager = flow_grid_new(TRUE); gtk_container_add(GTK_CONTAINER(self),priv->pager); pagers = g_list_prepend(pagers,self); g_object_set_data(G_OBJECT(priv->pager),"sort_numeric",GINT_TO_POINTER(TRUE)); return self; } void pager_add_pin ( GtkWidget *pager, gchar *pin ) { GObject *child; if(ipc_get()!=IPC_SWAY && ipc_get()!=IPC_HYPR) return g_free(pin); if(!g_list_find_custom(global_pins,pin,(GCompareFunc)g_strcmp0)) global_pins = g_list_prepend(global_pins,pin); child = G_OBJECT(base_widget_get_child(pager)); if(!child) return; if(!g_list_find_custom(g_object_get_data(child,"pins"),pin, (GCompareFunc)g_strcmp0)) g_object_set_data(child,"pins",g_list_prepend( g_object_get_data(child,"pins"),pin)); } void pager_invalidate_all ( workspace_t *ws ) { GList *iter; for(iter=pagers; iter; iter=g_list_next(iter)) flow_item_invalidate(flow_grid_find_child(iter->data,ws)); } static gint pager_comp_id ( workspace_t *a, gpointer id ) { return GPOINTER_TO_INT(a->id) - GPOINTER_TO_INT(id); } static gint pager_comp_name ( workspace_t *a, gchar * b) { return g_strcmp0(a->name,b); } workspace_t *pager_workspace_from_id ( gpointer id ) { GList *item; item = g_list_find_custom(workspaces,id,(GCompareFunc)pager_comp_id); if(item) return item->data; return NULL; } workspace_t *pager_workspace_from_name ( gchar *name ) { GList *item; item = g_list_find_custom(workspaces,name,(GCompareFunc)pager_comp_name); if(item) return item->data; return NULL; } gpointer pager_workspace_get_active ( GtkWidget *widget ) { GdkMonitor *mon; if(!actives) return NULL; mon = widget_get_monitor(widget); if(!mon) return NULL; return g_hash_table_lookup(actives, g_object_get_data(G_OBJECT(mon),"xdg_name")); } void pager_workspace_set_active ( workspace_t *ws, const gchar *output ) { GdkDisplay *gdisp; GdkMonitor *gmon; gchar *name; gint i; if(!output || !ws) return; if(!actives) actives = g_hash_table_new_full((GHashFunc)str_nhash, (GEqualFunc)str_nequal,g_free,NULL); gdisp = gdk_display_get_default(); for(i=gdk_display_get_n_monitors(gdisp)-1;i>=0;i--) { gmon = gdk_display_get_monitor(gdisp,i); name = g_object_get_data(G_OBJECT(gmon),"xdg_name"); if(name && !g_strcmp0(name,output)) g_hash_table_insert(actives,g_strdup(name),ws->id); } } gpointer pager_workspace_id_from_name ( const gchar *name ) { GList *iter; for(iter=workspaces;iter;iter=g_list_next(iter)) if(!g_strcmp0(name,((workspace_t *)iter->data)->name)) return ((workspace_t *)iter->data)->id; return NULL; } gboolean pager_workspace_is_focused ( workspace_t *ws ) { return (ws == focus); } gpointer pager_get_focused ( void ) { if(!focus) return NULL; else return focus->id; } void pager_workspace_set_focus ( gpointer id ) { workspace_t *ws; ws = pager_workspace_from_id(id); if(!ws || ws==focus) return; pager_invalidate_all(focus); focus = ws; pager_invalidate_all(focus); taskbar_invalidate_conditional(); } void pager_workspace_delete ( gpointer id ) { GList *iter, *item; workspace_t *ws; item = g_list_find_custom(workspaces,id, (GCompareFunc)pager_comp_id); if(!item) return; ws = item->data; if(g_list_find_custom(global_pins,ws->name,(GCompareFunc)g_strcmp0)) { ws->id = PAGER_PIN_ID; ws->visible = FALSE; for(iter=pagers;iter;iter=g_list_next(iter)) if(!g_list_find_custom(g_object_get_data( G_OBJECT(base_widget_get_child(iter->data)),"pins"), ws->name,(GCompareFunc)g_strcmp0)) flow_grid_delete_child(iter->data,ws); } else { g_list_foreach(pagers,(GFunc)flow_grid_delete_child,ws); g_free(ws->name); g_free(ws); workspaces = g_list_delete_link(workspaces,item); } } void pager_workspace_new ( workspace_t *new ) { workspace_t *ws; GList *iter; ws = pager_workspace_from_id(new->id); if(!ws) for(iter=workspaces;iter;iter=g_list_next(iter)) if(!g_strcmp0(((workspace_t *)iter->data)->name,new->name) && ((workspace_t *)iter->data)->id == PAGER_PIN_ID) ws = iter->data; if(!ws) { ws = g_malloc0(sizeof(workspace_t)); workspaces = g_list_prepend(workspaces,ws); } if(g_strcmp0(ws->name,new->name)) { g_free(ws->name); ws->name = g_strdup(new->name); pager_invalidate_all(ws); } if(ws->id != new->id || ws->visible != new->visible) { ws->id = new->id; ws->visible = new->visible; pager_invalidate_all(ws); } g_list_foreach(pagers,(GFunc)pager_item_new,ws); if(new->focused) pager_workspace_set_focus(ws->id); } void pager_populate ( void ) { GList *item; workspace_t *ws; if(!pagers) return; for(item = workspaces;item;item=g_list_next(item)) g_list_foreach(pagers,(GFunc)pager_item_new,item->data); for(item = global_pins;item;item=g_list_next(item)) if(!g_list_find_custom(workspaces,item->data, (GCompareFunc)pager_comp_name)) { ws = g_malloc(sizeof(workspace_t)); ws->id = PAGER_PIN_ID; ws->name = g_strdup(item->data); ws->visible = FALSE; workspaces = g_list_prepend(workspaces,ws); g_list_foreach(pagers,(GFunc)pager_item_new,ws); } g_list_foreach(pagers,(GFunc)flow_grid_update,NULL); } void pager_update ( void ) { g_list_foreach(pagers,(GFunc)flow_grid_update,NULL); } sfwbar-1.0~beta13/src/pager.h000066400000000000000000000035331450657570000161070ustar00rootroot00000000000000#ifndef __PAGER_H__ #define __PAGER_H__ #include "basewidget.h" #include "flowgrid.h" #define PAGER_TYPE (pager_get_type()) #define PAGER(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), PAGER_TYPE, Pager)) #define PAGER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), PAGER_TYPE, PagerClass)) #define IS_PAGER(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), PAGER_TYPE)) #define IS_PAGERCLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), PAGER_TYPE)) typedef struct _Pager Pager; typedef struct _PagerClass PagerClass; struct _Pager { BaseWidget item; }; struct _PagerClass { BaseWidgetClass parent_class; }; typedef struct _PagerPrivate PagerPrivate; struct _PagerPrivate { GtkWidget *pager; }; GType pager_get_type ( void ); typedef struct workspace_s { gpointer id; gchar *name; gboolean visible; gboolean focused; GtkWidget *pager; } workspace_t; struct pager_api { void (*set_workspace) ( workspace_t *); guint (*get_geom) ( workspace_t *, GdkRectangle **, GdkRectangle *, gint *); }; GtkWidget *pager_new(); workspace_t *pager_workspace_from_id ( gpointer id ); void pager_workspace_new ( workspace_t *new ); void pager_workspace_delete ( gpointer id ); void pager_workspace_set_focus ( gpointer id ); void pager_workspace_set_active ( workspace_t *ws, const gchar *output ); gpointer pager_workspace_get_active ( GtkWidget *widget ); gboolean pager_workspace_is_focused ( workspace_t *ws ); void pager_update ( void ); void pager_invalidate_all ( workspace_t *ws ); void pager_populate ( void ); void pager_add_pin ( GtkWidget *pager, gchar *pin ); gpointer pager_workspace_id_from_name ( const gchar *name ); void pager_api_register ( struct pager_api *new ); void pager_set_workspace ( workspace_t *ws ); guint pager_get_geom ( workspace_t *, GdkRectangle **, GdkRectangle *, gint * ); gpointer pager_get_focused ( void ); #endif sfwbar-1.0~beta13/src/pageritem.c000066400000000000000000000121161450657570000167560ustar00rootroot00000000000000/* This entire file is licensed under GNU General Public License v3.0 * * Copyright 2022- sfwbar maintainers */ #include "sfwbar.h" #include "pager.h" #include "pageritem.h" G_DEFINE_TYPE_WITH_CODE (PagerItem, pager_item, FLOW_ITEM_TYPE, G_ADD_PRIVATE (PagerItem)); void pager_item_update ( GtkWidget *self ) { PagerItemPrivate *priv; g_return_if_fail(IS_PAGER_ITEM(self)); priv = pager_item_get_instance_private(PAGER_ITEM(self)); if(!priv->invalid) return; if(g_strcmp0(gtk_button_get_label(GTK_BUTTON(priv->button)),priv->ws->name)) gtk_button_set_label(GTK_BUTTON(priv->button),priv->ws->name); gtk_widget_set_has_tooltip(priv->button, GPOINTER_TO_INT(g_object_get_data(G_OBJECT(priv->pager),"preview"))); if ( pager_workspace_is_focused(priv->ws) ) gtk_widget_set_name(priv->button, "pager_focused"); else if (priv->ws->visible) gtk_widget_set_name(priv->button, "pager_visible"); else gtk_widget_set_name(priv->button, "pager_normal"); gtk_widget_unset_state_flags(gtk_bin_get_child(GTK_BIN(self)), GTK_STATE_FLAG_PRELIGHT); flow_item_set_active(self, priv->ws->id != GINT_TO_POINTER(-1) || g_list_find_custom(g_object_get_data(G_OBJECT(priv->pager),"pins"), priv->ws->name, (GCompareFunc)g_strcmp0)!=NULL); priv->invalid = FALSE; } workspace_t *pager_item_get_workspace ( GtkWidget *self ) { PagerItemPrivate *priv; g_return_val_if_fail(IS_PAGER_ITEM(self),NULL); priv = pager_item_get_instance_private(PAGER_ITEM(self)); return priv->ws; } static void pager_item_invalidate ( GtkWidget *self ) { PagerItemPrivate *priv; if(!self) return; g_return_if_fail(IS_PAGER_ITEM(self)); priv = pager_item_get_instance_private(PAGER_ITEM(self)); flow_grid_invalidate(priv->pager); priv->invalid = TRUE; } static gint pager_item_compare ( GtkWidget *a, GtkWidget *b, GtkWidget *parent) { PagerItemPrivate *p1,*p2; g_return_val_if_fail(IS_PAGER_ITEM(a),0); g_return_val_if_fail(IS_PAGER_ITEM(b),0); p1 = pager_item_get_instance_private(PAGER_ITEM(a)); p2 = pager_item_get_instance_private(PAGER_ITEM(b)); if(g_object_get_data(G_OBJECT(parent),"sort_numeric")) return strtoll(p1->ws->name, NULL, 10)-strtoll(p2->ws->name, NULL, 10); else return g_strcmp0(p1->ws->name, p2->ws->name); } static void pager_item_class_init ( PagerItemClass *kclass ) { FLOW_ITEM_CLASS(kclass)->update = pager_item_update; FLOW_ITEM_CLASS(kclass)->get_parent = (void * (*)(GtkWidget *))pager_item_get_workspace; FLOW_ITEM_CLASS(kclass)->compare = pager_item_compare; FLOW_ITEM_CLASS(kclass)->invalidate = pager_item_invalidate; } static void pager_item_init ( PagerItem *self ) { } static void pager_item_button_cb( GtkWidget *self, gpointer data ) { pager_set_workspace(data); } static gboolean pager_item_draw_preview ( GtkWidget *widget, cairo_t *cr, workspace_t *ws ) { GtkStyleContext *style; GdkRGBA fg; guint w,h,i,n; gint focus; GdkRectangle *wins, spc; w = gtk_widget_get_allocated_width (widget); h = gtk_widget_get_allocated_height (widget); style = gtk_widget_get_style_context(widget); gtk_style_context_get_color (style,GTK_STATE_FLAG_NORMAL, &fg); cairo_set_line_width(cr,1); n = pager_get_geom(ws,&wins,&spc,&focus); if(!n) return TRUE; for(i=0;iws = ws; priv->pager = pager; priv->button = gtk_button_new_with_label(ws->name); gtk_container_add(GTK_CONTAINER(self),priv->button); gtk_widget_set_name(priv->button, "pager_normal"); g_signal_connect(priv->button,"clicked",G_CALLBACK(pager_item_button_cb),ws); g_signal_connect(priv->button,"query-tooltip", G_CALLBACK(pager_item_draw_tooltip),ws); g_object_ref(G_OBJECT(self)); flow_grid_add_child(pager,self); pager_item_invalidate(self); return self; } sfwbar-1.0~beta13/src/pageritem.h000066400000000000000000000017561450657570000167730ustar00rootroot00000000000000#ifndef __PAGERITEM_H__ #define __PAGERITEM_H__ #include "sfwbar.h" #include "flowitem.h" #include "pager.h" #define PAGER_ITEM_TYPE (pager_item_get_type()) #define PAGER_ITEM(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), PAGER_ITEM_TYPE, PagerItem)) #define PAGER_ITEM_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), PAGER_ITEM_TYPE, PagerItemClass)) #define IS_PAGER_ITEM(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), PAGER_ITEM_TYPE)) #define IS_PAGER_ITEMCLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), PAGER_ITEM_TYPE)) typedef struct _PagerItem PagerItem; typedef struct _PagerItemClass PagerItemClass; struct _PagerItem { FlowItem item; }; struct _PagerItemClass { FlowItemClass parent_class; }; typedef struct _PagerItemPrivate PagerItemPrivate; struct _PagerItemPrivate { GtkWidget *button; GtkWidget *pager; workspace_t *ws; gboolean invalid; }; GType pager_item_get_type ( void ); GtkWidget *pager_item_new( GtkWidget *pager, workspace_t *ws ); #endif sfwbar-1.0~beta13/src/popup.c000066400000000000000000000145251450657570000161520ustar00rootroot00000000000000/* This entire file is licensed under GNU General Public License v3.0 * * Copyright 2022- sfwbar maintainers */ #include #include "window.h" #include "grid.h" #include "config.h" #include "basewidget.h" #include "popup.h" #include "bar.h" static GHashTable *popup_list; void popup_get_gravity ( GtkWidget *widget, GdkGravity *wanchor, GdkGravity *manchor ) { switch(bar_get_toplevel_dir(widget)) { case GTK_POS_TOP: *wanchor = GDK_GRAVITY_SOUTH_WEST; *manchor = GDK_GRAVITY_NORTH_WEST; break; case GTK_POS_LEFT: *wanchor = GDK_GRAVITY_NORTH_EAST; *manchor = GDK_GRAVITY_NORTH_WEST; break; case GTK_POS_RIGHT: *wanchor = GDK_GRAVITY_NORTH_WEST; *manchor = GDK_GRAVITY_NORTH_EAST; break; default: *wanchor = GDK_GRAVITY_NORTH_WEST; *manchor = GDK_GRAVITY_SOUTH_WEST; break; } } static void popup_popdown ( GtkWidget * widget ) { if(window_ref_check(widget)) return; gtk_grab_remove(gtk_bin_get_child(GTK_BIN(widget))); gtk_widget_hide(widget); } static gboolean popup_button_cb ( GtkWidget *widget, GdkEventButton *ev, void *d ) { GdkWindow *window, *wparent; wparent = gtk_widget_get_window(d); window = ev->window; while(window && window != wparent) window = gdk_window_get_parent(window); if(window == wparent) return FALSE; popup_popdown(gtk_widget_get_ancestor(d, GTK_TYPE_WINDOW)); return TRUE; } static gboolean popup_state_cb ( GtkWidget *widget, GdkEventWindowState *ev, gpointer d ) { if( ev->changed_mask & GDK_WINDOW_STATE_WITHDRAWN && ev->new_window_state & GDK_WINDOW_STATE_WITHDRAWN) popup_popdown(widget); return FALSE; } static void popup_transfer_window_grab (GtkWidget *widget, GdkSeat *seat) { GdkWindow *window; GdkWindowAttr attributes; gint attributes_mask; GdkWindow *parent; static const GdkSeatCapabilities grab_caps = GDK_SEAT_CAPABILITY_KEYBOARD | GDK_SEAT_CAPABILITY_POINTER | GDK_SEAT_CAPABILITY_TABLET_STYLUS; if(gtk_window_get_type_hint(GTK_WINDOW(widget)) != GDK_WINDOW_TYPE_HINT_POPUP_MENU) return; attributes.x = -100; attributes.y = -100; attributes.width = 10; attributes.height = 10; attributes.window_type = GDK_WINDOW_TEMP; attributes.wclass = GDK_INPUT_OUTPUT; attributes.override_redirect = TRUE; attributes.event_mask = 0; attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_NOREDIR; parent = gdk_screen_get_root_window (gtk_widget_get_screen (widget)); window = gdk_window_new (parent, &attributes, attributes_mask); gtk_widget_register_window (widget, window); gdk_window_show (window); gdk_seat_grab(seat, window, grab_caps, TRUE, NULL, NULL, NULL, NULL); g_object_set_data (G_OBJECT (gtk_widget_get_window(widget)), "gdk-attached-grab-window", window); } void popup_show ( GtkWidget *parent, GtkWidget *popup, GdkEvent *ev ) { GdkRectangle rect; GtkWidget *child; GdkWindow *gparent, *gpopup, *transfer; GdkGravity wanchor, panchor; GdkSeat *seat; child = gtk_bin_get_child(GTK_BIN(popup)); if(!child) return; gtk_widget_show_all(child); gtk_widget_unrealize(popup); gtk_widget_realize(popup); gparent = gtk_widget_get_window(parent); gpopup = gtk_widget_get_window( gtk_widget_get_ancestor(popup,GTK_TYPE_WINDOW)); rect.x = 0; rect.y = 0; rect.width = gdk_window_get_width(gparent); rect.height = gdk_window_get_height(gparent); popup_get_gravity(parent,&wanchor,&panchor); window_ref(gtk_widget_get_ancestor(parent,GTK_TYPE_WINDOW),popup); g_object_set_data(G_OBJECT(popup), "parent_window", gtk_widget_get_ancestor(parent, GTK_TYPE_WINDOW)); if(ev && gdk_event_get_device(ev)) seat = gdk_device_get_seat(gdk_event_get_device(ev)); else seat = gdk_display_get_default_seat(gdk_display_get_default()); popup_transfer_window_grab(popup, seat); gtk_window_set_transient_for(GTK_WINDOW(popup), GTK_WINDOW(gtk_widget_get_ancestor(parent, GTK_TYPE_WINDOW))); gdk_window_set_transient_for(gpopup, gparent); gdk_window_move_to_rect(gpopup, &rect, wanchor, panchor, GDK_ANCHOR_FLIP_X | GDK_ANCHOR_FLIP_Y,0,0); css_widget_cascade(popup,NULL); transfer = g_object_get_data(G_OBJECT(gpopup), "gdk-attached-grab-window"); if(transfer) { gdk_seat_ungrab(seat); gtk_widget_unregister_window(popup, transfer); gdk_window_destroy(transfer); g_object_set_data (G_OBJECT (popup), "gdk-attached-grab-window", NULL); gtk_grab_add(child); } } void popup_trigger ( GtkWidget *parent, gchar *name, GdkEvent *ev ) { GtkWidget *popup; popup = popup_from_name(name); if(!popup || !parent) return; if(gtk_widget_get_visible(popup)) popup_popdown(popup); else popup_show(parent, popup, ev); } void popup_size_allocate_cb ( GtkWidget *grid, GdkRectangle *alloc, GtkWidget *win ) { GdkRectangle *saved; if(!gtk_widget_is_visible(win)) return; if(window_ref_check(win)) return; saved = g_object_get_data(G_OBJECT(win), "saved-alloc"); if(!saved) return; if(alloc->width == saved->width && alloc->height == saved->height) return; *saved = *alloc; gtk_widget_hide(win); gtk_widget_show(win); } void popup_set_autoclose ( GtkWidget *win, gboolean autoclose ) { if(autoclose) gtk_window_set_type_hint(GTK_WINDOW(win), GDK_WINDOW_TYPE_HINT_POPUP_MENU); else gtk_window_set_type_hint(GTK_WINDOW(win), GDK_WINDOW_TYPE_HINT_NORMAL); } GtkWidget *popup_new ( gchar *name ) { GtkWidget *win, *grid; if(!popup_list) popup_list = g_hash_table_new((GHashFunc)str_nhash,(GEqualFunc)str_nequal); win = popup_from_name(name); if(win) return win; win = gtk_window_new(GTK_WINDOW_POPUP); grid = grid_new(); gtk_container_add(GTK_CONTAINER(win),grid); gtk_widget_set_name(win,name); gtk_widget_set_name(grid,name); gtk_window_set_accept_focus(GTK_WINDOW(win), TRUE); g_signal_connect(grid, "button-release-event", G_CALLBACK(popup_button_cb), win); g_signal_connect(win,"window-state-event", G_CALLBACK(popup_state_cb),NULL); g_object_set_data(G_OBJECT(win), "saved-alloc", g_malloc0(sizeof(GdkRectangle))); g_signal_connect(grid, "size-allocate", G_CALLBACK(popup_size_allocate_cb), win); g_hash_table_insert(popup_list,g_strdup(name),win); return win; } GtkWidget *popup_from_name ( gchar *name ) { if(!popup_list || !name) return NULL; return g_hash_table_lookup(popup_list,name); } sfwbar-1.0~beta13/src/popup.h000066400000000000000000000006321450657570000161510ustar00rootroot00000000000000#ifndef __POPUP_H__ #define __POPUP_H__ GtkWidget *popup_new ( gchar *name ); GtkWidget *popup_from_name ( gchar *name ); void popup_trigger ( GtkWidget *parent, gchar *name, GdkEvent *ev ); void popup_show ( GtkWidget *parent, GtkWidget *popup, GdkEvent *ev ); void popup_get_gravity ( GtkWidget *widget, GdkGravity *, GdkGravity * ); void popup_set_autoclose ( GtkWidget *win, gboolean autoclose ); #endif sfwbar-1.0~beta13/src/scale.c000066400000000000000000000043241450657570000160720ustar00rootroot00000000000000/* This entire file is licensed under GNU General Public License v3.0 * * Copyright 2022- sfwbar maintainers */ #include "sfwbar.h" #include "basewidget.h" #include "scale.h" G_DEFINE_TYPE_WITH_CODE (Scale, scale, BASE_WIDGET_TYPE, G_ADD_PRIVATE (Scale)); static void scale_update_value ( GtkWidget *self ) { ScalePrivate *priv; gchar *value; g_return_if_fail(IS_SCALE(self)); priv = scale_get_instance_private(SCALE(self)); value = base_widget_get_value(self); if(!g_strrstr(value,"nan")) gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(priv->scale), g_ascii_strtod(value,NULL)); } static GtkWidget *scale_get_child ( GtkWidget *self ) { ScalePrivate *priv; g_return_val_if_fail(IS_SCALE(self),NULL); priv = scale_get_instance_private(SCALE(self)); return priv->scale; } static GtkWidget *scale_mirror ( GtkWidget *src ) { GtkWidget *self; g_return_val_if_fail(IS_SCALE(src),NULL); self = scale_new(); base_widget_copy_properties(self,src); return self; } static void scale_style_updated ( GtkWidget *widget, GtkWidget *self ) { ScalePrivate *priv; gint dir; priv = scale_get_instance_private(SCALE(self)); gtk_widget_style_get(priv->scale,"direction",&dir,NULL); if(priv->dir == dir) return; priv->dir = dir; gtk_orientable_set_orientation(GTK_ORIENTABLE(priv->scale), (dir==GTK_POS_LEFT || dir==GTK_POS_RIGHT)? GTK_ORIENTATION_HORIZONTAL:GTK_ORIENTATION_VERTICAL); gtk_progress_bar_set_inverted(GTK_PROGRESS_BAR(priv->scale), ( priv->dir==GTK_POS_TOP || priv->dir==GTK_POS_LEFT )); } static void scale_class_init ( ScaleClass *kclass ) { BASE_WIDGET_CLASS(kclass)->update_value = scale_update_value; BASE_WIDGET_CLASS(kclass)->get_child = scale_get_child; BASE_WIDGET_CLASS(kclass)->mirror = scale_mirror; } static void scale_init ( Scale *self ) { } GtkWidget *scale_new ( void ) { GtkWidget *self; ScalePrivate *priv; self = GTK_WIDGET(g_object_new(scale_get_type(), NULL)); priv = scale_get_instance_private(SCALE(self)); priv->scale = gtk_progress_bar_new(); gtk_container_add(GTK_CONTAINER(self),priv->scale); g_signal_connect(G_OBJECT(priv->scale),"style_updated", (GCallback)scale_style_updated,self); return self; } sfwbar-1.0~beta13/src/scale.h000066400000000000000000000014101450657570000160700ustar00rootroot00000000000000#ifndef __SCALE_H__ #define __SCALE_H__ #include "basewidget.h" #define SCALE_TYPE (scale_get_type()) #define SCALE(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), SCALE_TYPE, Scale)) #define SCALE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), SCALE_TYPE, ScaleClass)) #define IS_SCALE(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), SCALE_TYPE)) #define IS_SCALECLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), SCALE_TYPE)) typedef struct _Scale Scale; typedef struct _ScaleClass ScaleClass; struct _Scale { BaseWidget item; }; struct _ScaleClass { BaseWidgetClass parent_class; }; typedef struct _ScalePrivate ScalePrivate; struct _ScalePrivate { GtkWidget *scale; gint dir; }; GType scale_get_type ( void ); GtkWidget *scale_new(); #endif sfwbar-1.0~beta13/src/scaleimage.c000066400000000000000000000274011450657570000170760ustar00rootroot00000000000000/* This entire file is licensed under GNU General Public License v3.0 * * Copyright 2020- sfwbar maintainers */ #include "sfwbar.h" #include "scaleimage.h" #include #include G_DEFINE_TYPE_WITH_CODE (ScaleImage, scale_image, GTK_TYPE_IMAGE, G_ADD_PRIVATE (ScaleImage)); static void scale_image_get_preferred_width ( GtkWidget *self, gint *m, gint *n ) { GtkStyleContext *style; GtkStateFlags flags; GtkBorder border, padding, margin; gint w; g_return_if_fail(IS_SCALE_IMAGE(self)); style = gtk_widget_get_style_context(self); flags = gtk_style_context_get_state(style); gtk_style_context_get_border(style,flags,&border); gtk_style_context_get_padding(style,flags,&padding); gtk_style_context_get_margin(style,flags,&margin); gtk_style_context_get(style,flags,"min-width",&w,NULL); *m = (w?w:16) + border.left + border.right + padding.left + padding.right + margin.left + margin.right; *n = *m; } static void scale_image_get_preferred_height ( GtkWidget *self, gint *m, gint *n ) { GtkStyleContext *style; GtkStateFlags flags; GtkBorder border, padding, margin; gint h; g_return_if_fail(IS_SCALE_IMAGE(self)); style = gtk_widget_get_style_context(self); flags = gtk_style_context_get_state(style); gtk_style_context_get_border(style,flags,&border); gtk_style_context_get_padding(style,flags,&padding); gtk_style_context_get_margin(style,flags,&margin); gtk_style_context_get(style,flags,"min-height",&h,NULL); *m = (h?h:16) + border.top + border.bottom + padding.top + padding.bottom + margin.top + margin.bottom; *n = *m; } static void scale_image_surface_update ( GtkWidget *self, gint width, gint height ) { ScaleImagePrivate *priv; GdkPixbuf *buf, *tmp; GdkPixbufLoader *loader; gchar *fallback; gboolean aspect; priv = scale_image_get_instance_private(SCALE_IMAGE(self)); priv->fallback = FALSE; if(priv->ftype == SI_ICON) buf = gtk_icon_theme_load_icon(gtk_icon_theme_get_default(), priv->fname,MIN(width,height),0,NULL); else if(priv->ftype == SI_FILE && priv->fname) buf = gdk_pixbuf_new_from_file_at_scale(priv->fname,width,height,TRUE,NULL); else if(priv->ftype == SI_BUFF && priv->pixbuf) buf = g_object_ref(priv->pixbuf); else if (priv->ftype == SI_DATA && priv->file) { loader = gdk_pixbuf_loader_new(); gdk_pixbuf_loader_set_size(loader,width,height); GdkRGBA col; gtk_style_context_get_color(gtk_widget_get_style_context(self), GTK_STATE_FLAG_NORMAL,&col); gchar *svg; gchar *rgba; if(strstr(priv->file,"@theme_fg_color")) { rgba = g_strdup_printf("Rgba(%d,%d,%d,%f)",(gint)(col.red*256), (gint)(col.green*256),(gint)(col.blue*256),col.alpha); svg = str_replace(priv->file,"@theme_fg_color",rgba); g_free(rgba); } else svg = NULL; gdk_pixbuf_loader_write(loader,(guchar *)(svg?svg:priv->file), strlen(svg?svg:priv->file), NULL); gdk_pixbuf_loader_close(loader,NULL); buf = gdk_pixbuf_loader_get_pixbuf(loader); if(buf) buf = gdk_pixbuf_copy(buf); g_object_unref(G_OBJECT(loader)); g_free(svg); } else buf = NULL; if(!buf) { fallback = get_xdg_config_file("icons/misc/missing.svg",NULL); if(fallback) { buf = gdk_pixbuf_new_from_file_at_scale(fallback, width, height, TRUE, NULL); g_free(fallback); priv->fallback = TRUE; } } if(buf) { aspect = (gboolean)gdk_pixbuf_get_width(buf) / (gboolean)gdk_pixbuf_get_height(buf); if((gboolean)width/(gboolean)height > aspect) width = (gboolean)height * aspect; else if((gboolean)width/(gboolean)height < aspect) height = (gboolean)width / aspect; } if(buf && gdk_pixbuf_get_width(buf) != width && gdk_pixbuf_get_height(buf) != height ) { tmp = buf; buf = gdk_pixbuf_scale_simple(tmp, width, height, GDK_INTERP_BILINEAR); g_object_unref(G_OBJECT(tmp)); } cairo_surface_destroy(priv->cs); if(!buf) { priv->cs = NULL; return; } priv->width = width; priv->height = height; priv->cs = gdk_cairo_surface_create_from_pixbuf(buf,0, gtk_widget_get_window(self)); g_object_unref(G_OBJECT(buf)); } static gboolean scale_image_draw ( GtkWidget *self, cairo_t *cr ) { ScaleImagePrivate *priv; GdkRGBA col, *color = NULL; GtkStyleContext *style; GtkStateFlags flags; GtkBorder border, padding, margin; gboolean symbolic; gint width, height, scale; gdouble x_origin, y_origin; g_return_val_if_fail(IS_SCALE_IMAGE(self),-1); priv = scale_image_get_instance_private(SCALE_IMAGE(self)); style = gtk_widget_get_style_context(self); flags = gtk_style_context_get_state(style); gtk_style_context_get_border(style,flags,&border); gtk_style_context_get_padding(style,flags,&padding); gtk_style_context_get_margin(style,flags,&margin); width = gtk_widget_get_allocated_width(self); height = gtk_widget_get_allocated_height(self); gtk_render_background(style,cr,margin.left, margin.top, width - margin.left - margin.right, height - margin.top - margin.bottom); gtk_render_frame(style,cr,margin.left, margin.top, width - margin.left - margin.right, height - margin.top - margin.bottom); scale = gtk_widget_get_scale_factor(self); width = (width - border.left - border.right - padding.left - padding.right - margin.left - margin.right) * scale; height = (height - border.top - border.bottom - padding.top - padding.bottom - margin.top - margin.bottom) * scale; if( width < 1 || height < 1 ) return FALSE; if(!priv->cs || priv->width != width || priv->height != height ) scale_image_surface_update(self,width,height); if(!priv->cs) return FALSE; if(priv->file) g_debug("image: %s @ %d x %d",priv->ftype==SI_DATA?"svg":priv->file, width, height ); x_origin = margin.left + padding.left + border.left + (width - cairo_image_surface_get_width(priv->cs))/(2*scale); y_origin = margin.top + padding.top + border.top + (height - cairo_image_surface_get_height(priv->cs))/(2*scale); gtk_widget_style_get(self,"color",&color,NULL); if(!color) { gtk_widget_style_get(self,"symbolic",&symbolic,NULL); if(symbolic || priv->fallback || (priv->file && strlen(priv->file)>=9 && !g_strcmp0(priv->file+strlen(priv->file)-9,"-symbolic"))) { gtk_style_context_get_color(gtk_widget_get_style_context(self), GTK_STATE_FLAG_NORMAL,&col); col.alpha = 1.0; color = gdk_rgba_copy(&col); } } if(color) { cairo_set_source_rgba(cr,color->red,color->green,color->blue,color->alpha); cairo_mask_surface(cr, priv->cs, x_origin, y_origin); gdk_rgba_free(color); } else if(priv->cs) { cairo_set_source_surface(cr,priv->cs, x_origin, y_origin); cairo_paint(cr); } return TRUE; } static void scale_image_clear ( GtkWidget *self ) { ScaleImagePrivate *priv; priv = scale_image_get_instance_private(SCALE_IMAGE(self)); g_clear_pointer(&priv->fname,g_free); g_clear_pointer(&priv->file,g_free); g_clear_pointer(&priv->extra,g_free); g_clear_pointer(&priv->pixbuf,g_object_unref); g_clear_pointer(&priv->cs,cairo_surface_destroy); priv->ftype = SI_NONE; } static void scale_image_destroy ( GtkWidget *self ) { g_return_if_fail(IS_SCALE_IMAGE(self)); scale_image_clear(self); GTK_WIDGET_CLASS(scale_image_parent_class)->destroy(self); } static void scale_image_class_init ( ScaleImageClass *kclass ) { GtkWidgetClass *widget_class = GTK_WIDGET_CLASS(kclass); widget_class->destroy = scale_image_destroy; widget_class->draw = scale_image_draw; widget_class->get_preferred_width = scale_image_get_preferred_width; widget_class->get_preferred_height = scale_image_get_preferred_height; gtk_widget_class_install_style_property( widget_class, g_param_spec_boxed("color","image color", "draw image in this color using it's alpha channel as a mask", GDK_TYPE_RGBA,G_PARAM_READABLE)); gtk_widget_class_install_style_property( widget_class, g_param_spec_boolean("symbolic","symbolic icon", "treat image as a symbolic icon and apply theme specific color", FALSE,G_PARAM_READABLE)); } static void scale_image_init ( ScaleImage *self ) { ScaleImagePrivate *priv; g_return_if_fail(IS_SCALE_IMAGE(self)); priv = scale_image_get_instance_private(SCALE_IMAGE(self)); priv->file = NULL; priv->fname = NULL; priv->pixbuf = NULL; priv->cs = NULL; priv->width = 0; priv->height = 0; priv->fallback = FALSE; priv->ftype = SI_NONE; } GtkWidget *scale_image_new() { return GTK_WIDGET(g_object_new(scale_image_get_type(), NULL)); } void scale_image_set_pixbuf ( GtkWidget *self, GdkPixbuf *pb ) { ScaleImagePrivate *priv; g_return_if_fail(IS_SCALE_IMAGE(self)); priv = scale_image_get_instance_private(SCALE_IMAGE(self)); if(priv->pixbuf == pb) return; scale_image_clear(self); priv->pixbuf = pb; priv->ftype = SI_BUFF; gtk_widget_queue_draw(self); } static void scale_image_check_appinfo ( GtkWidget *self, GtkIconTheme *theme, const gchar *icon ) { ScaleImagePrivate *priv; GDesktopAppInfo *app; GtkIconInfo *info; gchar *file; priv = scale_image_get_instance_private(SCALE_IMAGE(self)); if(priv->ftype != SI_NONE) return; app = g_desktop_app_info_new(icon); if(!app) return; if( !g_desktop_app_info_get_nodisplay(app) ) { file = g_desktop_app_info_get_string(app,"Icon"); if( (info = gtk_icon_theme_lookup_icon(theme,file,10,0)) ) { g_object_unref(G_OBJECT(info)); priv->fname = g_strdup(file); priv->ftype = SI_ICON; } g_free(file); } g_object_unref(G_OBJECT(app)); } static gboolean scale_image_check_icon ( GtkWidget *self, const gchar *icon ) { ScaleImagePrivate *priv; GtkIconTheme *theme; GtkIconInfo *info; gint i,j; gchar *temp; gchar ***desktop; theme = gtk_icon_theme_get_default(); if(!theme) return FALSE; priv = scale_image_get_instance_private(SCALE_IMAGE(self)); if( (info = gtk_icon_theme_lookup_icon(theme,icon,10,0)) ) { g_object_unref(G_OBJECT(info)); priv->fname = g_strdup(icon); priv->ftype = SI_ICON; return TRUE; } desktop = g_desktop_app_info_search(icon); for(j=0;desktop[j];j++) { for(i=0;desktop[j][i];i++) scale_image_check_appinfo(self, theme, desktop[j][i]); g_strfreev(desktop[j]); } g_free(desktop); if(priv->ftype!=SI_NONE) return TRUE; temp = g_strconcat(icon,".desktop",NULL); scale_image_check_appinfo(self, theme, temp); g_free(temp); return (priv->ftype!=SI_NONE); } void scale_image_set_image ( GtkWidget *self, const gchar *image, gchar *extra ) { static gchar *exts[4] = {"", ".svg", ".png", ".xpm"}; ScaleImagePrivate *priv; GdkPixbuf *buf; gint i; gchar *temp,*test; g_return_if_fail(IS_SCALE_IMAGE(self)); priv = scale_image_get_instance_private( SCALE_IMAGE(self)); if(!image) return; if( !g_strcmp0(priv->file,image) && !g_strcmp0(priv->extra,extra) ) return; scale_image_clear(self); priv->file = g_strdup(image); priv->extra = g_strdup(extra); gtk_widget_queue_draw(self); if(!g_ascii_strncasecmp(priv->file,"ftype = SI_DATA; return; } if(scale_image_check_icon(self,image)) return; temp = g_ascii_strdown(image,-1); scale_image_check_icon(self,temp); g_free(temp); if(priv->ftype == SI_ICON) return; for(i=0;i<4;i++) { test = g_strconcat(priv->file,exts[i],NULL); temp = get_xdg_config_file(test,extra); g_free(test); if(temp) { buf = gdk_pixbuf_new_from_file_at_scale(temp,10,10,TRUE,NULL); if(buf) { g_object_unref(G_OBJECT(buf)); g_free(priv->fname); priv->fname = temp; priv->ftype = SI_FILE; break; } else g_free(temp); } } } sfwbar-1.0~beta13/src/scaleimage.h000066400000000000000000000023701450657570000171010ustar00rootroot00000000000000#ifndef __SCALEIMAGE_H__ #define __SCALEIMAGE_H__ #include #define SCALE_IMAGE_TYPE (scale_image_get_type()) #define SCALE_IMAGE(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), SCALE_IMAGE_TYPE, ScaleImage)) #define SCALE_IMAGE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), SCALE_IMAGE_TYPE, ScaleImageClass)) #define IS_SCALE_IMAGE(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), SCALE_IMAGE_TYPE)) #define IS_SCALE_IMAGE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), SCALE_IMAGE_TYPE)) typedef struct _ScaleImage ScaleImage; typedef struct _ScaleImageClass ScaleImageClass; struct _ScaleImage { GtkImage parent_class; }; struct _ScaleImageClass { GtkImageClass parent_class; }; typedef struct _ScaleImagePrivate ScaleImagePrivate; struct _ScaleImagePrivate { gint ftype; gint width, height; gboolean fallback; gchar *file; gchar *extra; gchar *fname; GdkPixbuf *pixbuf; cairo_surface_t *cs; }; enum { SI_NONE, SI_ICON, SI_FILE, SI_BUFF, SI_DATA }; GType scale_image_get_type ( void ); void scale_image_set_image ( GtkWidget *, const gchar *, gchar *); GtkWidget *scale_image_new(); int scale_image_update ( GtkWidget *widget ); void scale_image_set_pixbuf ( GtkWidget *widget, GdkPixbuf * ); #endif sfwbar-1.0~beta13/src/scanner.c000066400000000000000000000262321450657570000164360ustar00rootroot00000000000000/* This entire file is licensed under GNU General Public License v3.0 * * Copyright 2020- sfwbar maintainers */ #include #include #include #include #include "sfwbar.h" #include "expr.h" #include "config.h" #include "client.h" static GList *file_list; static GHashTable *scan_list; static GHashTable *trigger_list; void scanner_file_attach ( gchar *trigger, ScanFile *file ) { if(!trigger_list) trigger_list = g_hash_table_new((GHashFunc)str_nhash, (GEqualFunc)str_nequal); g_hash_table_insert(trigger_list,trigger,file); } ScanFile *scanner_file_get ( gchar *trigger ) { if(!trigger_list) return NULL; return g_hash_table_lookup(trigger_list,trigger); } ScanFile *scanner_file_new ( gint source, gchar *fname, gchar *trigger, gint flags ) { ScanFile *file; GList *iter; if(source == SO_CLIENT) iter = NULL; else for(iter=file_list;iter;iter=g_list_next(iter)) if(!g_strcmp0(fname,((ScanFile *)(iter->data))->fname)) break; if(iter) { file = iter->data; g_free(fname); } else { file = g_malloc0(sizeof(ScanFile)); file_list = g_list_append(file_list,file); file->fname = fname; } file->source = source; file->flags = flags; if( !strchr(file->fname,'*') && !strchr(file->fname,'?') ) file->flags |= VF_NOGLOB; if(g_strcmp0(file->trigger,trigger)) { if(file->trigger) g_hash_table_remove(trigger_list,file->trigger); g_free(file->trigger); file->trigger = trigger; if(file->trigger) scanner_file_attach(file->trigger,file); } else g_free(trigger); return file; } void scanner_var_free ( ScanVar *var ) { if(var->file) var->file->vars = g_list_remove(var->file->vars,var); if(var->type != G_TOKEN_REGEX) g_free(var->definition); else if(var->definition) g_regex_unref(var->definition); expr_cache_free(var->expr); g_free(var->str); g_free(var); } void scanner_var_new ( gchar *name, ScanFile *file, gchar *pattern, guint type, gint flag ) { ScanVar *var, *old; if(!name) return; old = scan_list? g_hash_table_lookup(scan_list, name): NULL; if(old && type != G_TOKEN_SET && old->file != file) return; var = old? old: g_malloc0(sizeof(ScanVar)); var->file = file; var->type = type; var->multi = flag; var->invalid = TRUE; switch(var->type) { case G_TOKEN_SET: expr_cache_free(var->expr); var->expr = expr_cache_new(); var->expr->definition = g_strdup(pattern); var->expr->eval = TRUE; var->vstate = 1; expr_dep_trigger(name); break; case G_TOKEN_JSON: g_free(var->definition); var->definition = g_strdup(pattern); break; case G_TOKEN_REGEX: if(var->definition) g_regex_unref(var->definition); var->definition = g_regex_new(pattern, 0, 0, NULL); break; } if(file && !old) file->vars = g_list_append(file->vars, var); if(!scan_list) scan_list = g_hash_table_new_full((GHashFunc)str_nhash, (GEqualFunc)str_nequal, g_free, (GDestroyNotify)scanner_var_free); if(!old) { g_hash_table_insert(scan_list, g_strdup(name), var); expr_dep_trigger(name); } } void scanner_var_invalidate ( void *key, ScanVar *var, void *data ) { if( !var->file || var->file->source != SO_CLIENT ) var->invalid = TRUE; } /* expire all variables in the tree */ void scanner_invalidate ( void ) { if(scan_list) g_hash_table_foreach(scan_list,(GHFunc)scanner_var_invalidate,NULL); } void scanner_var_values_update ( ScanVar *var, gchar *value) { if(!value) return; if(var->multi!=G_TOKEN_FIRST || !var->count) { g_free(var->str); var->str = value; switch(var->multi) { case G_TOKEN_SUM: var->val += g_ascii_strtod(var->str,NULL); break; case G_TOKEN_PRODUCT: var->val *= g_ascii_strtod(var->str,NULL); break; case G_TOKEN_LASTW: var->val = g_ascii_strtod(var->str,NULL); break; case G_TOKEN_FIRST: if(!var->count) var->val = g_ascii_strtod(var->str,NULL); break; } var->count++; } else g_free(value); var->invalid = FALSE; } void scanner_update_json ( struct json_object *obj, ScanFile *file ) { GList *node; struct json_object *ptr; gint i; for(node=file->vars;node!=NULL;node=g_list_next(node)) { ptr = jpath_parse(((ScanVar *)node->data)->definition,obj); if(ptr && json_object_is_type(ptr, json_type_array)) for(i=0;idata), g_strdup(json_object_get_string(json_object_array_get_idx(ptr,i)))); } if(ptr) json_object_put(ptr); } } /* update variables in a specific file (or pipe) */ GIOStatus scanner_file_update ( GIOChannel *in, ScanFile *file, gsize *size ) { ScanVar *var; GList *node; GMatchInfo *match; struct json_tokener *json = NULL; struct json_object *obj; gchar *read_buff; GIOStatus status; gsize lsize; if(size) *size = 0; while((status = g_io_channel_read_line(in,&read_buff,&lsize,NULL,NULL)) ==G_IO_STATUS_NORMAL) { if(size) *size += lsize; for(node=file->vars;node!=NULL;node=g_list_next(node)) { var=node->data; switch(var->type) { case G_TOKEN_REGEX: if(var->definition && g_regex_match (var->definition, read_buff, 0, &match)) scanner_var_values_update(var,g_match_info_fetch (match, 1)); if(match) g_match_info_free (match); break; case G_TOKEN_GRAB: scanner_var_values_update(var,g_strdup(read_buff)); break; case G_TOKEN_JSON: if(!json) json = json_tokener_new(); break; } } if(json) obj = json_tokener_parse_ex(json,read_buff, strlen(read_buff)); g_free(read_buff); } g_free(read_buff); if(json) { scanner_update_json(obj,file); json_object_put(obj); json_tokener_free(json); } for(node=file->vars;node!=NULL;node=g_list_next(node)) { ((ScanVar *)node->data)->invalid = FALSE; ((ScanVar *)node->data)->vstate = TRUE; } g_debug("channel status %d, (%s)",status,file->fname?file->fname:"(null)"); return status; } void scanner_var_reset ( ScanVar *var, gpointer dummy ) { gint64 tv = g_get_monotonic_time(); var->pval = var->val; var->count = 0; var->val = 0; var->time = tv-var->ptime; var->ptime = tv; } time_t scanner_file_mtime ( glob_t *gbuf ) { gint i; struct stat stattr; time_t res = 0; for(i=0;gbuf->gl_pathv[i]!=NULL;i++) if(!stat(gbuf->gl_pathv[i],&stattr)) res = MAX(stattr.st_mtime, res); return res; } gboolean scanner_file_exec ( ScanFile *file ) { GIOChannel *chan; gint out; gchar **argv; if(!g_shell_parse_argv(file->fname, NULL, &argv, NULL)) return FALSE; if(!g_spawn_async_with_pipes(NULL,argv,NULL,G_SPAWN_SEARCH_PATH,NULL,NULL, NULL, NULL, &out, NULL, NULL)) return FALSE; chan = g_io_channel_unix_new(out); if(chan) { g_debug("scanner: exec '%s'",file->fname); g_list_foreach(file->vars,(GFunc)scanner_var_reset,NULL); (void)scanner_file_update(chan,file,NULL); g_io_channel_unref(chan); } close(out); return TRUE; } /* update all variables in a file (by glob) */ gboolean scanner_file_glob ( ScanFile *file ) { glob_t gbuf; gchar *dnames[2]; struct stat stattr; gint i; gint in; GIOChannel *chan; gboolean reset=FALSE; if(!file) return FALSE; if(file->source == SO_CLIENT || !file->fname) return FALSE; if(file->source == SO_EXEC) return scanner_file_exec(file); if((file->flags & VF_NOGLOB)||(file->source != SO_FILE)) { dnames[0] = file->fname; dnames[1] = NULL; gbuf.gl_pathv = dnames; } else if(glob(file->fname,GLOB_NOSORT,NULL,&gbuf)) { globfree(&gbuf); return FALSE; } if( !(file->flags & VF_CHTIME) || (file->mtime < scanner_file_mtime(&gbuf)) ) for(i=0;gbuf.gl_pathv[i];i++) { in = open(gbuf.gl_pathv[i],O_RDONLY); if(in != -1) { if(!reset) { reset=TRUE; g_list_foreach(file->vars,(GFunc)scanner_var_reset,NULL); } chan = g_io_channel_unix_new(in); (void)scanner_file_update(chan,file,NULL); g_io_channel_unref(chan); close(in); if(!stat(gbuf.gl_pathv[i],&stattr)) file->mtime = stattr.st_mtime; } } if(!(file->flags & VF_NOGLOB)&&(file->source == SO_FILE)) globfree(&gbuf); return TRUE; } gchar *scanner_parse_identifier ( gchar *id, gchar **fname ) { gchar *ptr; if(!id) { if(fname) *fname = NULL; return NULL; } if(*id=='$') id = id + sizeof(gchar); ptr=strchr(id,'.'); if(fname) *fname = g_strdup(ptr?ptr:".val"); if(ptr) return g_strndup(id,ptr-id); else return g_strdup(id); } ScanVar *scanner_var_update ( gchar *name, gboolean update, ExprCache *expr ) { ScanVar *var; if(!scan_list) return NULL; var = g_hash_table_lookup(scan_list, name); if(!var) return NULL; if(!update || !var->invalid) { expr->vstate = expr->vstate || var->vstate; return var; } if(var->type == G_TOKEN_SET) { if(!var->inuse) { var->inuse = TRUE; var->expr->parent = expr; (void)expr_cache_eval(var->expr); var->expr->parent = NULL; var->inuse = FALSE; var->vstate = var->expr->vstate; expr->vstate = expr->vstate || var->expr->vstate; scanner_var_reset(var,NULL); scanner_var_values_update(var,g_strdup(var->expr->cache)); var->invalid = FALSE; } } else { scanner_file_glob(var->file); expr->vstate = TRUE; var->vstate = TRUE; } return var; } /* get value of a variable by name */ void *scanner_get_value ( gchar *ident, gboolean update, ExprCache *expr ) { ScanVar *var; double *retval; gchar *fname,*id; id = scanner_parse_identifier(ident,&fname); var = scanner_var_update(id, update, expr); g_free(id); if(!var) { g_free(fname); expr_dep_add(ident, expr); if(*ident == '$') return g_strdup(""); else return g_malloc0(sizeof(gdouble)); } if(var->type == G_TOKEN_SET) expr_dep_add(ident, expr); if(*ident == '$') { g_debug("scanner: %s = \"%s\" (vstate: %d)",ident,var->str,expr->vstate); g_free(fname); if(var->str) return g_strdup(var->str); else return g_strdup(""); } retval = g_malloc0(sizeof(gdouble)); if(!g_strcmp0(fname,".val")) *retval = var->val; else if(!g_strcmp0(fname,".pval")) *retval = var->pval; else if(!g_strcmp0(fname,".count")) *retval = var->count; else if(!g_strcmp0(fname,".time")) *retval = var->time; else if(!g_strcmp0(fname,".age")) *retval = (g_get_monotonic_time() - var->ptime); g_free(fname); g_debug("scanner: %s = %f (vstate: %d)",ident,*retval,expr->vstate); return retval; } gboolean scanner_is_variable ( gchar *identifier ) { gchar *name; gboolean result; if(!scan_list) return FALSE; name = scanner_parse_identifier(identifier,NULL); result = (g_hash_table_lookup(scan_list, name)!=NULL); g_free(name); return result; } sfwbar-1.0~beta13/src/scanner.h000066400000000000000000000024211450657570000164350ustar00rootroot00000000000000#ifndef __SCANNER_H__ #define __SCANNER_H__ #include #include "expr.h" enum { SO_FILE = 0, SO_EXEC = 1, SO_CLIENT = 2 }; enum { VF_CHTIME = 1, VF_NOGLOB = 2 }; typedef struct scan_file { gchar *fname; gchar *trigger; gint flags; guchar source; time_t mtime; GList *vars; void *client; } ScanFile; typedef struct scan_var { ExprCache *expr; void *definition; gchar *str; guint vstate; double val; double pval; gint64 time; gint64 ptime; gint count; gint multi; guint type; gboolean invalid; gboolean inuse; ScanFile *file; } ScanVar; void scanner_invalidate ( void ); void scanner_var_reset ( ScanVar *var, gpointer dummy ); void scanner_update_json ( struct json_object *, ScanFile * ); GIOStatus scanner_file_update ( GIOChannel *, ScanFile *, gsize * ); int scanner_glob_file ( ScanFile * ); void *scanner_get_value ( gchar *ident, gboolean update, ExprCache *expr ); void scanner_var_new ( gchar *, ScanFile *, gchar *, guint, gint ); gchar *scanner_parse_identifier ( gchar *id, gchar **fname ); ScanFile *scanner_file_get ( gchar *trigger ); ScanFile *scanner_file_new ( gint , gchar *, gchar *, gint ); gboolean scanner_is_variable ( gchar *identifier ); void scanner_file_attach ( gchar *trigger, ScanFile *file ); #endif sfwbar-1.0~beta13/src/sfwbar.c000066400000000000000000000140431450657570000162660ustar00rootroot00000000000000/* This entire file is licensed under GNU General Public License v3.0 * * Copyright 2020- sfwbar maintainers */ #include #include #include "sfwbar.h" #include "wayland.h" #include "bar.h" #include "tray.h" #include "taskbar.h" #include "pager.h" #include "switcher.h" #include "config.h" #include "sway_ipc.h" #include "expr.h" gchar *confname; gchar *sockname; static gchar *cssname; static gchar *monitor; static gchar *bar_id; static gchar *dfilter; static GRegex *rfilter; static gboolean debug = FALSE; static enum ipc_type ipc; static gchar **sargv; static GOptionEntry entries[] = { {"config",'f',0,G_OPTION_ARG_FILENAME,&confname,"Specify config file"}, {"css",'c',0,G_OPTION_ARG_FILENAME,&cssname,"Specify css file"}, {"socket",'s',0,G_OPTION_ARG_FILENAME,&sockname,"Specify sway socket file"}, {"debug",'d',0,G_OPTION_ARG_NONE,&debug,"Display debug info"}, {"debug-filter",'g',0,G_OPTION_ARG_STRING,&dfilter,"Filter debug output for a pattern"}, {"monitor",'m',0,G_OPTION_ARG_STRING,&monitor, "Monitor to display the panel on (use \"-m list\" to list monitors`"}, {"bar_id",'b',0,G_OPTION_ARG_STRING,&bar_id, "default sway bar_id to listen on for sway events"}, {NULL}}; void parse_command_line ( gint argc, gchar **argv) { GOptionContext *optc; optc = g_option_context_new(" - S* Floating Window Bar"); g_option_context_add_main_entries(optc,entries,NULL); g_option_context_add_group(optc, gtk_get_option_group(TRUE)); g_option_context_parse(optc,&argc,&argv,NULL); g_option_context_free(optc); } GdkMonitor *widget_get_monitor ( GtkWidget *self ) { GdkWindow *win; GdkDisplay *disp; g_return_val_if_fail(GTK_IS_WIDGET(self),NULL); win = gtk_widget_get_window(self); if(!win) return NULL; disp = gdk_window_get_display(win); if(!disp) return NULL; return gdk_display_get_monitor_at_window(disp,win); } void list_monitors ( void ) { GdkDisplay *gdisp; GdkMonitor *gmon; gint nmon,i; gchar *name; gdisp = gdk_display_get_default(); nmon = gdk_display_get_n_monitors(gdisp); for(i=0;idata)))) { gtk_application_add_window(app,GTK_WINDOW(iter->data)); css_widget_cascade(GTK_WIDGET(iter->data),NULL); base_widget_autoexec(iter->data,NULL); if(monitor) bar_set_monitor(iter->data, monitor); } g_list_free(clist); gdisp = gdk_display_get_default(); g_signal_connect(gdisp, "monitor-added", G_CALLBACK(bar_monitor_added_cb),NULL); g_signal_connect(gdisp, "monitor-removed", G_CALLBACK(bar_monitor_removed_cb),NULL); g_thread_unref(g_thread_new("scanner", (GThreadFunc)base_widget_scanner_thread, g_main_context_get_thread_default())); action_function_exec("SfwBarInit",NULL,NULL,NULL,NULL); g_timeout_add (100,(GSourceFunc )shell_timer,NULL); g_unix_signal_add(SIGUSR1,(GSourceFunc)switcher_event,NULL); g_unix_signal_add(SIGUSR2,(GSourceFunc)bar_visibility_toggle_all,NULL); g_unix_signal_add(SIGHUP,(GSourceFunc)sfwbar_restart,NULL); signal_triggers_add(); } int main (int argc, gchar **argv) { GtkApplication *app; gint status, i; sargv = g_malloc0(sizeof(gchar *)*(argc+1)); for(i=0; i #include #include #include "scanner.h" enum ipc_type { IPC_SWAY = 1, IPC_HYPR = 2, IPC_WAYLAND = 3 }; void hypr_ipc_init ( void ); enum ipc_type ipc_get ( void ); void ipc_set ( enum ipc_type new ); void css_init ( gchar * ); void css_file_load ( gchar * ); void css_widget_apply ( GtkWidget *widget, gchar *css ); void css_widget_cascade ( GtkWidget *widget, gpointer data ); void css_add_class ( GtkWidget *widget, gchar *css_class ); void css_remove_class ( GtkWidget *widget, gchar *css_class ); struct json_object *jpath_parse ( gchar *path, struct json_object *obj ); void widget_set_css ( GtkWidget *, gpointer ); void widget_parse_css ( GtkWidget *widget, gchar *css ); GdkMonitor *widget_get_monitor ( GtkWidget *self ); gint socket_connect ( const gchar *sockaddr, gint to ); json_object *recv_json ( gint sock, gint32 len ); void list_remove_link ( GList **list, void *child ); gchar *get_xdg_config_file ( gchar *fname, gchar *extra ); const gchar *json_string_by_name ( struct json_object *obj, gchar *name ); gint64 json_int_by_name ( struct json_object *obj, gchar *name, gint64 defval); gboolean json_bool_by_name ( struct json_object *obj, gchar *name, gboolean defval); gdouble json_double_by_name ( struct json_object *obj, gchar *name, gdouble defval); gboolean pattern_match ( gchar **dict, gchar *string ); gboolean regex_match_list ( GList *dict, gchar *string ); int md5_file( gchar *path, guchar output[16] ); struct rect parse_rect ( struct json_object *obj ); guint str_nhash ( gchar *str ); gboolean str_nequal ( gchar *str1, gchar *str2 ); gchar *str_replace ( gchar *str, gchar *old, gchar *new ); #endif sfwbar-1.0~beta13/src/sni.c000066400000000000000000000241041450657570000155720ustar00rootroot00000000000000/* This entire file is licensed under GNU General Public License v3.0 * * Copyright 2021- sfwbar maintainers */ #include #include #include #include #include "sfwbar.h" #include "trayitem.h" #include "tray.h" struct watcher_item { SniWatcher *watcher; gchar *uid; guint id; }; static const gchar sni_watcher_xml[] = "" " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " ""; GDBusConnection *sni_get_connection ( void ) { static GDBusConnection *con; if(con) return con; con = g_bus_get_sync(G_BUS_TYPE_SESSION,NULL,NULL); return con; } static void sni_watcher_item_free ( struct watcher_item *item ) { g_free(item->uid); g_free(item); } static void sni_watcher_item_lost_cb ( GDBusConnection *con, const gchar *name, struct watcher_item *item ) { g_debug("sni watcher %s: lost item: %s",item->watcher->iface,name); g_dbus_connection_emit_signal(con,NULL,"/StatusNotifierWatcher", item->watcher->iface,"StatusNotifierItemUnregistered", g_variant_new("(s)",item->uid),NULL); g_bus_unwatch_name(item->id); item->watcher->items = g_list_delete_link(item->watcher->items, g_list_find(item->watcher->items,item)); sni_watcher_item_free(item); } static gint sni_watcher_item_add ( SniWatcher *watcher, gchar *uid ) { gchar *name; struct watcher_item *item; GList *iter; for(iter=watcher->items;iter;iter=g_list_next(iter)) if(!g_strcmp0(((struct watcher_item *)iter->data)->uid,uid)) return -1; item = g_malloc0(sizeof(struct watcher_item)); item->watcher = watcher; item->uid = g_strdup(uid); if(strchr(uid,'/')) name = g_strndup(uid,strchr(uid,'/')-uid); else name = g_strdup(uid); g_debug("sni watcher %s: watching item %s", watcher->iface, name); item->id = g_bus_watch_name(G_BUS_TYPE_SESSION,name, G_BUS_NAME_WATCHER_FLAGS_NONE,NULL, (GBusNameVanishedCallback)sni_watcher_item_lost_cb,item ,NULL); watcher->items = g_list_prepend(watcher->items,item); g_free(name); return 0; } static void sni_watcher_method(GDBusConnection *con, const gchar *sender, const gchar *path, const gchar *iface, const gchar *method, GVariant *parameters, GDBusMethodInvocation *invocation, SniWatcher *watcher) { const gchar *parameter; gchar *name; g_variant_get (parameters, "(&s)", ¶meter); g_dbus_method_invocation_return_value(invocation,NULL); if(!g_strcmp0(method,"RegisterStatusNotifierItem")) { if(*parameter!='/') name = g_strdup(parameter); else name = g_strconcat(sender,parameter,NULL); if(sni_watcher_item_add(watcher,name)!=-1) g_dbus_connection_emit_signal(con,NULL,"/StatusNotifierWatcher", watcher->iface,"StatusNotifierItemRegistered", g_variant_new("(s)",name),NULL); g_free(name); } else if(!g_strcmp0(method,"RegisterStatusNotifierHost")) { watcher->watcher_registered = TRUE; g_debug("sni watcher %s: registered host %s",watcher->iface,parameter); g_dbus_connection_emit_signal(con,NULL,"/StatusNotifierWatcher", watcher->iface,"StatusNotifierHostRegistered",NULL,NULL); } } static GVariant *sni_watcher_get_prop (GDBusConnection *con, const gchar *sender, const gchar *path, const gchar *iface, const gchar *prop,GError **error, SniWatcher *watcher ) { GVariant *ret = NULL; GVariantBuilder *builder; GList *iter; if (!g_strcmp0 (prop,"IsStatusNotifierHostRegistered")) ret = g_variant_new_boolean(TRUE); else if(!g_strcmp0 (prop,"ProtocolVersion")) ret = g_variant_new_int32(0); else if(!g_strcmp0 (prop,"RegisteredStatusNotifierItems")) { if(!watcher->items) ret = g_variant_new_array(G_VARIANT_TYPE_STRING,NULL,0); else { builder = g_variant_builder_new(G_VARIANT_TYPE_ARRAY); for(iter=watcher->items;iter;iter=g_list_next(iter)) g_variant_builder_add_value(builder, g_variant_new_string(((struct watcher_item*)iter->data)->uid)); ret = g_variant_builder_end (builder); g_variant_builder_unref (builder); } } return ret; } static const GDBusInterfaceVTable watcher_vtable = { (GDBusInterfaceMethodCallFunc)sni_watcher_method, (GDBusInterfaceGetPropertyFunc)sni_watcher_get_prop, NULL }; void sni_watcher_unregister_cb ( GDBusConnection *con, const gchar *name, SniWatcher *watcher) { if(watcher->regid) g_dbus_connection_unregister_object(con, watcher->regid); watcher->regid = 0; g_list_free_full(watcher->items,(GDestroyNotify)sni_watcher_item_free); g_debug("sni watcher %s unregistered",watcher->iface); } void sni_watcher_register_cb ( GDBusConnection *con, const gchar *name, SniWatcher *watcher) { GList *iter; if(watcher->regid) g_dbus_connection_unregister_object(con, watcher->regid); watcher->regid = g_dbus_connection_register_object (con, "/StatusNotifierWatcher", watcher->idata->interfaces[0], &watcher_vtable, watcher, NULL, NULL); g_bus_own_name(G_BUS_TYPE_SESSION,watcher->iface, G_BUS_NAME_OWNER_FLAGS_NONE,NULL,NULL, (GBusNameLostCallback)sni_watcher_unregister_cb,watcher,NULL); for(iter=watcher->host->items;iter;iter=g_list_next(iter)) sni_watcher_item_add(watcher,((SniItem *)iter->data)->uid); g_debug("sni watcher %s registered",watcher->iface); } static void sni_host_item_new ( GDBusConnection *con, SniHost *host, const gchar *uid) { SniItem *sni; GList *iter; for(iter=host->items;iter;iter=g_list_next(iter)) if(!g_strcmp0(((SniItem *)iter->data)->uid,uid)) return; sni = sni_item_new(con,host,uid); host->items = g_list_prepend(host->items,sni); g_debug("sni host %s: item registered: %s %s",host->iface,sni->dest, sni->path); } void sni_host_item_registered_cb ( GDBusConnection* con, const gchar* sender, const gchar* path, const gchar* iface, const gchar* signal, GVariant* parameters, gpointer data) { const gchar *parameter; g_variant_get (parameters, "(&s)", ¶meter); sni_host_item_new(con,data,parameter); } static void sni_host_list_cb ( GDBusConnection *con, GAsyncResult *res, SniHost *host) { GVariant *result,*inner; GVariantIter *iter; gchar *str; result = g_dbus_connection_call_finish(con, res, NULL); if(!result) return; g_variant_get(result, "(v)",&inner); if(inner) { iter = g_variant_iter_new(inner); while(g_variant_iter_next(iter,"&s",&str)) sni_host_item_new(con, host, str); g_variant_iter_free(iter); g_variant_unref(inner); } g_variant_unref(result); } static void sni_host_item_unregistered_cb ( GDBusConnection* con, const gchar* sender, const gchar* path, const gchar* interface, const gchar* signal, GVariant* parameters, SniHost *host ) { const gchar *parameter; GList *item; g_variant_get (parameters, "(&s)", ¶meter); g_debug("sni host %s: unregister item %s",host->iface, parameter); for(item=host->items;item;item=g_list_next(item)) if(!g_strcmp0(((SniItem *)item->data)->uid,parameter)) break; if(!item) return; g_debug("sni host %s: removing item %s",host->iface,parameter); sni_item_free(item->data); host->items = g_list_delete_link(host->items,item); } static void sni_host_register_cb ( GDBusConnection *con, const gchar *name, const gchar *owner_name, SniWatcher *watcher ) { SniHost *host = watcher->host; g_dbus_connection_call(con, host->watcher, "/StatusNotifierWatcher", host->watcher, "RegisterStatusNotifierHost", g_variant_new("(s)", host->iface), G_VARIANT_TYPE ("(a{sv})"), G_DBUS_CALL_FLAGS_NONE,-1,NULL,NULL,host); g_dbus_connection_signal_subscribe(con,NULL,watcher->iface, "StatusNotifierItemRegistered","/StatusNotifierWatcher",NULL, G_DBUS_SIGNAL_FLAGS_NONE,sni_host_item_registered_cb,host,NULL); g_dbus_connection_signal_subscribe(con,NULL,watcher->iface, "StatusNotifierItemUnregistered","/StatusNotifierWatcher",NULL, G_DBUS_SIGNAL_FLAGS_NONE, (GDBusSignalCallback)sni_host_item_unregistered_cb,host,NULL); g_dbus_connection_call(con, host->watcher, "/StatusNotifierWatcher", "org.freedesktop.DBus.Properties", "Get",g_variant_new("(ss)", host->watcher,"RegisteredStatusNotifierItems"), NULL, G_DBUS_CALL_FLAGS_NONE,-1,NULL,(GAsyncReadyCallback)sni_host_list_cb,host); g_debug("sni host %s: found watcher %s (%s)",host->iface,name,owner_name); } static void sni_register ( gchar *name ) { SniWatcher*watcher; SniHost*host; gchar *xml; watcher = g_malloc0(sizeof(SniWatcher)); host = g_malloc0(sizeof(SniHost)); xml = g_strdup_printf(sni_watcher_xml,name); watcher->idata = g_dbus_node_info_new_for_xml (xml, NULL); g_free(xml); if(!watcher->idata) g_error("SNI: introspection error"); watcher->iface = g_strdup_printf("org.%s.StatusNotifierWatcher",name); host->item_iface = g_strdup_printf("org.%s.StatusNotifierItem",name); host->iface = g_strdup_printf("org.%s.StatusNotifierHost-%d",name,getpid()); host->watcher = watcher->iface; watcher->host = host; g_bus_watch_name(G_BUS_TYPE_SESSION,watcher->iface, G_BUS_NAME_WATCHER_FLAGS_NONE, (GBusNameAppearedCallback)sni_host_register_cb, (GBusNameVanishedCallback)sni_watcher_register_cb, watcher,NULL); g_bus_own_name(G_BUS_TYPE_SESSION,host->iface, G_BUS_NAME_OWNER_FLAGS_NONE,NULL,NULL,NULL,NULL,NULL); } void sni_init ( void ) { if(!sni_get_connection()) return; sni_register("kde"); sni_register("freedesktop"); } sfwbar-1.0~beta13/src/sni.h000066400000000000000000000025371450657570000156050ustar00rootroot00000000000000#ifndef __SNI_H__ #define __SNI_H__ #define SNI_MAX_STRING 11 #define SNI_MAX_PROP 19 enum { SNI_PROP_CATEGORY = 0, SNI_PROP_ID = 1, SNI_PROP_TITLE = 2, SNI_PROP_STATUS = 3, SNI_PROP_ICON = 4, SNI_PROP_OVLAY = 5, SNI_PROP_ATTN = 6, SNI_PROP_ATTNMOV = 7, SNI_PROP_LABEL = 8, SNI_PROP_LGUIDE = 9, SNI_PROP_THEME = 10, SNI_PROP_ICONPIX = 11, SNI_PROP_OVLAYPIX = 12, SNI_PROP_ATTNPIX = 13, SNI_PROP_WINDOWID = 14, SNI_PROP_TOOLTIP = 15, SNI_PROP_ISMENU = 16, SNI_PROP_MENU = 17, SNI_PROP_ORDER = 18 }; typedef struct sni_host { gchar *iface; gchar *watcher; gchar *item_iface; GList *items; } SniHost; typedef struct sni_watcher { guint regid; gboolean watcher_registered; gchar *iface; GList *items; GDBusNodeInfo *idata; SniHost *host; } SniWatcher; typedef struct sni_item { gchar *uid; gchar *udest; gchar *dest; gchar *path; gchar *string[SNI_MAX_STRING]; gchar *menu_path; GdkPixbuf *pixbuf[3]; gboolean menu; gboolean dirty; gint ref; guint signal; guint32 order; GCancellable *cancel; SniHost *host; } SniItem; void sni_init ( void ); void sni_update ( void ); GDBusConnection *sni_get_connection ( void ); void sni_get_menu ( GtkWidget *widget, GdkEvent *event ); SniItem *sni_item_new (GDBusConnection *, SniHost *, const gchar *); void sni_item_free ( SniItem *sni ); #endif sfwbar-1.0~beta13/src/sniitem.c000066400000000000000000000144631450657570000164600ustar00rootroot00000000000000/* This entire file is licensed under GNU General Public License v3.0 * * Copyright 2022- sfwbar maintainers */ #include #include #include #include #include "sfwbar.h" #include "trayitem.h" #include "tray.h" #include "scaleimage.h" struct sni_prop_wrapper { guint prop; SniItem *sni; }; static gchar *sni_properties[] = { "Category", "Id", "Title", "Status", "IconName", "OverlayIconName", "AttentionIconName", "AttentionMovieName", "XAyatanaLabel", "XAyatanaLabelGuide", "IconThemePath", "IconPixmap", "OverlayIconPixmap", "AttentionIconPixmap", "ToolTip", "WindowId", "ItemIsMenu", "Menu", "XAyatanaOrderingIndex" }; GdkPixbuf *sni_item_get_pixbuf ( GVariant *v ) { gint32 x,y; guint32 *ptr; cairo_surface_t *cs; GdkPixbuf *res; gint i; GVariant *img,*child; gsize len; if(!v || !g_variant_check_format_string(v,"a(iiay)",FALSE) || g_variant_n_children(v) < 1) return NULL; child = g_variant_get_child_value(v,0); g_variant_get(child,"(ii@ay)",&x,&y,&img); ptr = (guint32 *)g_variant_get_fixed_array(img,&len,sizeof(guchar)); if(!len || !ptr || len != x*y*4) { g_variant_unref(img); g_variant_unref(child); return NULL; } ptr = g_memdup2(ptr,len); g_variant_unref(img); g_variant_unref(child); for(i=0;isni->ref--; result = g_dbus_connection_call_finish(con, res, NULL); if(result) g_variant_get(result, "(v)",&inner); if(!result || !inner) return g_free(wrap); g_variant_unref(result); if(wrap->prop<=SNI_PROP_THEME && g_variant_is_of_type(inner,G_VARIANT_TYPE_STRING)) { g_free(wrap->sni->string[wrap->prop]); g_variant_get(inner,"s",&(wrap->sni->string[wrap->prop])); g_debug("sni %s: property %s = %s",wrap->sni->dest, sni_properties[wrap->prop],wrap->sni->string[wrap->prop]); } else if((wrap->prop>=SNI_PROP_ICONPIX)&&(wrap->prop<=SNI_PROP_ATTNPIX)) { if(wrap->sni->pixbuf[wrap->prop-SNI_PROP_ICONPIX]) g_object_unref(wrap->sni->pixbuf[wrap->prop-SNI_PROP_ICONPIX]); wrap->sni->pixbuf[wrap->prop-SNI_PROP_ICONPIX] = sni_item_get_pixbuf(inner); g_debug("sni %s: property %s received",wrap->sni->dest, sni_properties[wrap->prop]); } else if(wrap->prop == SNI_PROP_MENU && g_variant_is_of_type(inner,G_VARIANT_TYPE_OBJECT_PATH)) { g_free(wrap->sni->menu_path); g_variant_get(inner,"o",&(wrap->sni->menu_path)); g_debug("sni %s: property %s = %s",wrap->sni->dest, sni_properties[wrap->prop],wrap->sni->menu_path); } else if(wrap->prop == SNI_PROP_ISMENU) { g_variant_get(inner,"b",&(wrap->sni->menu)); g_debug("sni %s: property %s = %d",wrap->sni->dest, sni_properties[wrap->prop],wrap->sni->menu); } else if(wrap->prop == SNI_PROP_ORDER) { g_variant_get(inner,"u",&(wrap->sni->order)); g_debug("sni %s: property %s = %u",wrap->sni->dest, sni_properties[wrap->prop],wrap->sni->order); } g_variant_unref(inner); tray_invalidate_all(wrap->sni); g_free(wrap); } void sni_item_get_prop ( GDBusConnection *con, SniItem *sni, guint prop ) { struct sni_prop_wrapper *wrap; wrap = g_malloc(sizeof(struct sni_prop_wrapper)); wrap->prop = prop; wrap->sni = sni; wrap->sni->ref++; g_dbus_connection_call(con, sni->dest, sni->path, "org.freedesktop.DBus.Properties", "Get", g_variant_new("(ss)", sni->host->item_iface, sni_properties[prop]),NULL, G_DBUS_CALL_FLAGS_NONE,-1,sni->cancel, (GAsyncReadyCallback)sni_item_prop_cb,wrap); } void sni_item_signal_cb (GDBusConnection *con, const gchar *sender, const gchar *path, const gchar *interface, const gchar *signal, GVariant *parameters, gpointer data) { g_debug("sni: received signal %s from %s",signal,sender); if(!g_strcmp0(signal,"NewTitle")) sni_item_get_prop(con,data,SNI_PROP_TITLE); else if(!g_strcmp0(signal,"NewStatus")) sni_item_get_prop(con,data,SNI_PROP_STATUS); else if(!g_strcmp0(signal,"NewToolTip")) sni_item_get_prop(con,data,SNI_PROP_TOOLTIP); else if(!g_strcmp0(signal,"NewIconThemePath")) sni_item_get_prop(con,data,SNI_PROP_THEME); else if(!g_strcmp0(signal,"NewIcon")) { sni_item_get_prop(con,data,SNI_PROP_ICON); sni_item_get_prop(con,data,SNI_PROP_ICONPIX); } else if(!g_strcmp0(signal,"NewOverlayIcon")) { sni_item_get_prop(con,data,SNI_PROP_OVLAY); sni_item_get_prop(con,data,SNI_PROP_OVLAYPIX); } else if(!g_strcmp0(signal,"NewAttentionIcon")) { sni_item_get_prop(con,data,SNI_PROP_ATTN); sni_item_get_prop(con,data,SNI_PROP_ATTNPIX); } else if(!g_strcmp0(signal,"XAyatanaNewLabel")) sni_item_get_prop(con,data,SNI_PROP_LABEL); } SniItem *sni_item_new (GDBusConnection *con, SniHost *host, const gchar *uid) { SniItem *sni; gchar *path; guint i; sni = g_malloc0(sizeof(SniItem)); sni->uid = g_strdup(uid); sni->cancel = g_cancellable_new(); sni->menu = TRUE; path = strchr(uid,'/'); if(path!=NULL) { sni->dest = g_strndup(uid,path-uid); sni->path = g_strdup(path); } else { sni->path = g_strdup("/StatusNotifierItem"); sni->dest = g_strdup(uid); } sni->host = host; sni->signal = g_dbus_connection_signal_subscribe(con,sni->dest, sni->host->item_iface,NULL,sni->path,NULL,0,sni_item_signal_cb,sni,NULL); tray_item_init_for_all(sni); for(i=0;isignal); tray_item_destroy(sni); g_cancellable_cancel(sni->cancel); g_object_unref(sni->cancel); for(i=0;i<3;i++) if(sni->pixbuf[i]!=NULL) g_object_unref(sni->pixbuf[i]); for(i=0;istring[i]); g_free(sni->menu_path); g_free(sni->uid); g_free(sni->path); g_free(sni->dest); g_free(sni); } sfwbar-1.0~beta13/src/snimenu.c000066400000000000000000000161511450657570000164620ustar00rootroot00000000000000/* This entire file is licensed under GNU General Public License v3.0 * * Copyright 2022- sfwbar maintainers */ #include #include #include #include "sfwbar.h" #include "sni.h" #include "menu.h" #include "flowitem.h" struct sni_menu_wrapper { GdkEvent *event; GtkWidget *widget; SniItem *sni; }; gint32 sni_variant_get_int32 ( GVariant *dict, gchar *key, gint def ) { GVariant *ptr; gint result; ptr = g_variant_lookup_value(dict,key,G_VARIANT_TYPE_INT32); if(ptr) { result = g_variant_get_int32(ptr); g_variant_unref(ptr); return result; } return def; } const gchar *sni_variant_get_string ( GVariant *dict, gchar *key, gchar *def ) { GVariant *ptr; const gchar *result; ptr = g_variant_lookup_value(dict,key,G_VARIANT_TYPE_STRING); if(ptr) { result = g_variant_get_string(ptr,NULL); g_variant_unref(ptr); return result; } return def; } gboolean sni_variant_get_bool ( GVariant *dict, gchar *key, gboolean def ) { GVariant *ptr; gboolean result; ptr = g_variant_lookup_value(dict,key,G_VARIANT_TYPE_BOOLEAN); if(ptr) { result = g_variant_get_boolean(ptr); g_variant_unref(ptr); return result; } return def; } GtkWidget *sni_variant_get_pixbuf ( GVariant *dict, gchar *key ) { GVariant *ptr; GdkPixbufLoader *loader; GdkPixbuf *pixbuf; GtkWidget *img = NULL; guchar *buff; gsize len; ptr = g_variant_lookup_value(dict,key,G_VARIANT_TYPE_ARRAY); if(!ptr) return NULL; buff = (guchar *)g_variant_get_fixed_array(ptr,&len,sizeof(guchar)); if(buff && len>0) { loader = gdk_pixbuf_loader_new(); gdk_pixbuf_loader_write(loader, buff, len, NULL); gdk_pixbuf_loader_close(loader,NULL); pixbuf = gdk_pixbuf_loader_get_pixbuf(loader); if(pixbuf) img = gtk_image_new_from_pixbuf(pixbuf); g_object_unref(G_OBJECT(loader)); } g_variant_unref(ptr); return img; } void sni_menu_item_cb ( GtkWidget *item, SniItem *sni ) { gint32 *id = g_object_get_data(G_OBJECT(item),"sni_id"); if(!id) return; g_debug("sni menu call: %d (%s) %s",*id, gtk_menu_item_get_label(GTK_MENU_ITEM(item)),sni->dest); g_dbus_connection_call(sni_get_connection(), sni->dest, sni->menu_path, "com.canonical.dbusmenu", "Event", g_variant_new("(isvu)", *id, "clicked", g_variant_new_int32(0), gtk_get_current_event_time()), NULL, G_DBUS_CALL_FLAGS_NONE, -1, NULL, NULL, NULL); } void sni_menu_item_decorate ( GtkWidget *item, GVariant *dict ) { GtkWidget *box; GtkWidget *wlabel; GtkWidget *child; const gchar *label, *icon; GtkWidget *img; gtk_widget_set_name(item,"tray"); if(GTK_IS_SEPARATOR_MENU_ITEM(item)) return; child = gtk_bin_get_child(GTK_BIN(item)); if(child) gtk_container_remove(GTK_CONTAINER(item),child); box = gtk_grid_new(); icon = sni_variant_get_string(dict,"icon-name",NULL); if(icon) img = gtk_image_new_from_icon_name(icon,GTK_ICON_SIZE_MENU); else img = sni_variant_get_pixbuf(dict,"icon-data"); if(img) gtk_grid_attach(GTK_GRID(box),img,1,1,1,1); label = sni_variant_get_string(dict,"label",""); if(label) { wlabel = gtk_label_new_with_mnemonic(label); gtk_grid_attach(GTK_GRID(box),wlabel,2,1,1,1); } gtk_container_add(GTK_CONTAINER(item),box); } GtkWidget *sni_get_menu_iter ( GVariant *list, struct sni_menu_wrapper *wrap) { GVariantIter iter; GVariant *item,*tmp; GtkWidget *mitem, *menu, *smenu; GVariant *dict,*idv; const gchar *type, *toggle, *children; gint32 active,*id; GSList *group = NULL; if(!list) return NULL; g_variant_iter_init(&iter,list); menu = gtk_menu_new(); gtk_menu_set_reserve_toggle_size(GTK_MENU(menu), FALSE); gtk_widget_set_name(menu,"tray"); while( (item = g_variant_iter_next_value(&iter)) ) { if(g_variant_is_of_type(item,G_VARIANT_TYPE_VARIANT)) { tmp = item; item = g_variant_get_variant(tmp); g_variant_unref(tmp); } dict = g_variant_get_child_value(item, 1); mitem = NULL; if(sni_variant_get_bool(dict,"visible",TRUE)) { type = sni_variant_get_string(dict,"type","standard"); toggle = sni_variant_get_string(dict,"toggle-type",""); children = sni_variant_get_string(dict,"children-display",""); active = sni_variant_get_int32(dict,"toggle-state",0); if(!g_strcmp0(children,"submenu")) { smenu = sni_get_menu_iter(g_variant_get_child_value(item, 2),wrap); if(smenu) { mitem = gtk_menu_item_new(); gtk_menu_item_set_submenu(GTK_MENU_ITEM(mitem),smenu); } } if(!mitem && !g_strcmp0(type,"separator")) mitem = gtk_separator_menu_item_new(); if(!mitem && !*toggle ) mitem= gtk_menu_item_new(); if(!mitem && !g_strcmp0(toggle,"checkmark")) { mitem= gtk_check_menu_item_new(); gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(mitem),active); } if(!mitem && !g_strcmp0(toggle,"radio")) { mitem= gtk_radio_menu_item_new(group); group = gtk_radio_menu_item_get_group(GTK_RADIO_MENU_ITEM(mitem)); } } if(mitem) { sni_menu_item_decorate(mitem,dict); gtk_widget_set_sensitive(mitem,sni_variant_get_bool(dict,"enabled",TRUE)); idv = g_variant_get_child_value(item, 0); id = g_malloc(sizeof(gint32)); *id = g_variant_get_int32(idv); g_variant_unref(idv); g_object_set_data_full(G_OBJECT(mitem),"sni_id",id,g_free); g_signal_connect(G_OBJECT(mitem),"activate", G_CALLBACK(sni_menu_item_cb),wrap->sni); gtk_container_add(GTK_CONTAINER(menu),mitem); } g_variant_unref(dict); g_variant_unref(item); } g_variant_unref(list); return menu; } void sni_get_menu_cb ( GObject *src, GAsyncResult *res, gpointer data ) { GVariant *result, *layout, *list=NULL; struct sni_menu_wrapper *wrap = data; GtkWidget *menu; gchar *tmp; result = g_dbus_connection_call_finish(G_DBUS_CONNECTION(src),res,NULL); if(result) { tmp = g_variant_print(result,TRUE); g_debug("sni %s: menu: %s",wrap->sni->dest,tmp); g_free(tmp); layout = g_variant_get_child_value(result, 1); if(layout) { list = g_variant_get_child_value(layout, 2); g_variant_unref(layout); } } menu = sni_get_menu_iter(list,wrap); if(menu) { g_object_ref_sink(G_OBJECT(menu)); g_signal_connect(G_OBJECT(menu),"unmap",G_CALLBACK(g_object_unref),NULL); menu_popup(wrap->widget,menu,wrap->event,NULL,NULL); } gdk_event_free(wrap->event); g_free(wrap); if(result) g_variant_unref(result); } void sni_get_menu ( GtkWidget *widget, GdkEvent *event ) { SniItem *sni; struct sni_menu_wrapper *wrap = g_malloc(sizeof(struct sni_menu_wrapper)); sni = flow_item_get_parent(widget); wrap->event = gdk_event_copy(event); wrap->sni = sni; wrap->widget = widget; g_debug("sni %s: requesting menu",wrap->sni->dest); g_dbus_connection_call(sni_get_connection(), sni->dest, sni->menu_path, "com.canonical.dbusmenu", "GetLayout", g_variant_new("(iias)", 0, -1, NULL), NULL, G_DBUS_CALL_FLAGS_NONE, -1, NULL, sni_get_menu_cb, wrap); } sfwbar-1.0~beta13/src/sway_ipc.c000066400000000000000000000377651450657570000166400ustar00rootroot00000000000000/* This entire file is licensed under GNU General Public License v3.0 * * Copyright 2020- sfwbar maintainers */ #include #include #include #include #include #include #include "sfwbar.h" #include "bar.h" #include "pager.h" #include "switcher.h" #include "wintree.h" static gint main_ipc; static const gint8 magic[6] = {0x69, 0x33, 0x2d, 0x69, 0x70, 0x63}; static ScanFile *sway_file; extern gchar *sockname; static json_object *sway_ipc_poll ( gint sock, gint32 *etype ) { static gchar buf[14]; guint32 plen; size_t pos; ssize_t rlen; for(pos=0;pos<14;pos+=rlen) if( (rlen = recv(sock,(gchar *)buf+pos,14-pos,0)) <=0 ) break; if(pos!=14) return NULL; memcpy(etype,buf+sizeof(magic)+sizeof(plen),sizeof(*etype)); memcpy(&plen,buf+sizeof(magic),sizeof(plen)); return recv_json(sock,plen); } static int sway_ipc_open (int to) { const gchar *socket_path; if(sockname!=NULL) socket_path=sockname; else socket_path = g_getenv("SWAYSOCK"); if (socket_path == NULL) return -1; return socket_connect(socket_path, to); } static int sway_ipc_send ( gint sock, gint32 type, gchar *command ) { gint8 sway_ipc_header[14]; gint32 plen = strlen(command); memcpy(sway_ipc_header,magic,sizeof(magic)); memcpy(sway_ipc_header+sizeof(magic),&plen,sizeof(plen)); memcpy(sway_ipc_header+sizeof(magic)+sizeof(plen),&type,sizeof(type)); if( write(sock,sway_ipc_header,sizeof(sway_ipc_header))==-1 ) return -1; if(plen>0) if( write(sock,command,plen)==-1 ) return -1; return 0; } void sway_ipc_command ( gchar *cmd, ... ) { va_list args; gchar *buf; if(!cmd) return; va_start(args,cmd); buf = g_strdup_vprintf(cmd,args); sway_ipc_send ( main_ipc, 0, buf); g_free(buf); va_end(args); } static json_object *sway_ipc_request ( gchar *command, gint32 type, gint32 *etype ) { gint sock; json_object *json; sock = sway_ipc_open(3000); if(sock==-1) return NULL; sway_ipc_send(sock,type,command); json = sway_ipc_poll(sock,etype); close(sock); return json; } static GdkRectangle sway_ipc_parse_rect ( struct json_object *obj ) { struct json_object *rect; GdkRectangle ret; json_object_object_get_ex(obj,"rect",&rect); ret.x = json_int_by_name(rect,"x",0); ret.y = json_int_by_name(rect,"y",0); ret.width = json_int_by_name(rect,"width",0); ret.height = json_int_by_name(rect,"height",0); return ret; } static void sway_minimized_set ( struct json_object *obj, const gchar *parent, const gchar *monitor ) { window_t *win; win = wintree_from_id( GINT_TO_POINTER(json_int_by_name(obj,"id",G_MININT64))); if(!win) return; if(!g_strcmp0(parent,"__i3_scratch")) win->state |= WS_MINIMIZED; else { win->state &= ~WS_MINIMIZED; g_clear_pointer(&win->workspace, g_free); win->workspace = g_strdup(parent); } if(!g_list_find_custom(win->outputs,monitor,(GCompareFunc)g_strcmp0) && g_strcmp0(monitor,"__i3")) { g_list_free_full(win->outputs,g_free); win->outputs = g_list_prepend(NULL,g_strdup(monitor)); wintree_commit(win); } } static void sway_set_state ( struct json_object *container) { window_t *win; win = wintree_from_id( GINT_TO_POINTER(json_int_by_name(container,"id",G_MININT64))); if(win) { if(json_int_by_name(container,"fullscreen_mode",0)) win->state |= WS_FULLSCREEN | WS_MAXIMIZED; else win->state &= ~ (WS_FULLSCREEN | WS_MAXIMIZED); } } static struct json_object *placement_find_wid ( struct json_object *obj, gint64 wid ) { json_object *ptr,*item,*arr, *ret; gint i; if(json_object_object_get_ex(obj,"floating_nodes",&arr)) if(json_object_is_type(arr, json_type_array)) for(i=0;iuid = wid; win->pid = json_int_by_name(container,"pid",G_MININT64); wintree_window_append(win); wintree_set_app_id(wid,app_id); wintree_set_title(wid,json_string_by_name(container,"name")); wintree_set_float(wid,!g_strcmp0( json_string_by_name(container,"type"),"floating_con")); wintree_log(wid); if(json_bool_by_name(container,"focused",FALSE)) wintree_set_focus(wid); sway_ipc_window_place(GPOINTER_TO_INT(wid), win->pid ); } static void sway_traverse_tree ( struct json_object *obj, const gchar *parent, const gchar *monitor, gboolean init) { struct json_object *iter,*arr,*ptr; gint i; json_object_object_get_ex(obj,"floating_nodes",&arr); if( arr && json_object_is_type(arr, json_type_array) ) for(i=0;iname = g_strdup(json_string_by_name(obj,"name")); ws->id = GINT_TO_POINTER(json_int_by_name(obj,"id",0)); ws->visible = json_bool_by_name(obj,"visible",FALSE); ws->focused = json_bool_by_name(obj,"focused",FALSE); return ws; } static void sway_ipc_pager_event ( struct json_object *obj ) { const gchar *change; struct json_object *current; workspace_t *ws; json_object_object_get_ex(obj,"current",¤t); if(!current) return; ws = sway_ipc_parse_workspace(current); change = json_string_by_name(obj,"change"); if(!g_strcmp0(change,"empty")) pager_workspace_delete(ws->id); else pager_workspace_new(ws); if(!g_strcmp0(change,"focus") || !g_strcmp0(change,"move")) pager_workspace_set_active(ws,json_string_by_name(current,"output")); if(!g_strcmp0(change,"focus")) pager_workspace_set_focus(ws->id); pager_update(); g_free(ws->name); g_free(ws); } static void sway_ipc_pager_populate ( void ) { gint32 etype; struct json_object *robj; gint i; workspace_t *ws; robj = sway_ipc_request("",1,&etype); if(!robj || !json_object_is_type(robj,json_type_array)) return; for(i=0;ivisible) pager_workspace_set_active(ws, json_string_by_name(json_object_array_get_idx(robj,i),"output")); g_free(ws->name); g_free(ws); } json_object_put(robj); } static gboolean sway_ipc_event ( GIOChannel *chan, GIOCondition cond, gpointer data ) { static gchar *ename[] = { "workspace", "", "mode", "window", "barconfig_update", "binding", "shutdown", "tick", "","","","","","","","","","","","", "bar_state_update", "input" }; struct json_object *obj,*container; struct json_object *scan; const gchar *change; gint32 etype; gpointer *wid; if(main_ipc==-1) return FALSE; obj = sway_ipc_poll(main_ipc,&etype); while (obj) { if(etype==0x80000000) sway_ipc_pager_event(obj); if(etype==0x80000004) { bar_set_visibility(NULL,json_string_by_name(obj,"id"), *(json_string_by_name(obj,"mode"))); if(g_strcmp0(json_string_by_name(obj,"hidden_state"),"hide")) { sway_ipc_command("bar %s hidden_state hide", json_string_by_name(obj,"id")); switcher_event(NULL); } } if(etype==0x00000004) // This is to capture minimized state on sway sway_traverse_tree(obj,NULL,NULL,FALSE); if(etype==0x80000003 && obj) { change = json_string_by_name(obj,"change"); if(change) { json_object_object_get_ex(obj,"container",&container); wid = GINT_TO_POINTER(json_int_by_name(container,"id",G_MININT64)); if(!g_strcmp0(change,"new")) sway_window_new (container); else if(!g_strcmp0(change,"close")) wintree_window_delete(wid); else if(!g_strcmp0(change,"title")) wintree_set_title(wid,json_string_by_name(container,"name")); else if(!g_strcmp0(change,"focus")) { wintree_set_focus(wid); sway_ipc_send(main_ipc,4,""); } else if(!g_strcmp0(change,"fullscreen_mode")) sway_set_state(container); else if(!g_strcmp0(change,"move")) sway_ipc_send(main_ipc,4,""); else if(!g_strcmp0(change,"floating")) wintree_set_float(wid,!g_strcmp0( json_string_by_name(container,"type"),"floating_con")); } } if(etype==0x80000014) bar_set_visibility(NULL,json_string_by_name(obj,"id"), json_bool_by_name(obj,"visible_by_modifier",FALSE)?'v':'x'); if(sway_file && etype>=0x80000000 && etype<=0x80000015) { scan = json_object_new_object(); json_object_object_add_ex(scan,ename[etype-0x80000000],obj,0); g_list_foreach(sway_file->vars,(GFunc)scanner_var_reset,NULL); scanner_update_json (scan,sway_file); json_object_get(obj); json_object_put(scan); base_widget_emit_trigger("sway"); } json_object_put(obj); obj = sway_ipc_poll(main_ipc,&etype); } return TRUE; } static void sway_ipc_minimize ( gpointer id ) { window_t *win; if(wintree_get_disown()) { win = wintree_from_id(id); if(win) g_clear_pointer(&win->workspace, g_free); } sway_ipc_command("[con_id=%d] move window to scratchpad", GPOINTER_TO_INT(id)); } static void sway_ipc_unminimize ( gpointer id ) { window_t *win; win = wintree_from_id(id); if(win && win->workspace) sway_ipc_command("[con_id=%d] move window to workspace %s", GPOINTER_TO_INT(id),win->workspace); else sway_ipc_command("[con_id=%d] focus",GPOINTER_TO_INT(id)); } static void sway_ipc_maximize ( gpointer id ) { sway_ipc_command("[con_id=%d] fullscreen enable",GPOINTER_TO_INT(id)); } static void sway_ipc_unmaximize ( gpointer id ) { sway_ipc_command("[con_id=%d] fullscreen disable",GPOINTER_TO_INT(id)); } static void sway_ipc_focus ( gpointer id ) { window_t *win; win = wintree_from_id(id); if(win && win->workspace) sway_ipc_command("[con_id=%d] move window to workspace %s", GPOINTER_TO_INT(id),win->workspace); sway_ipc_command("[con_id=%d] focus",GPOINTER_TO_INT(id)); } static void sway_ipc_close ( gpointer id ) { sway_ipc_command("[con_id=%d] kill",GPOINTER_TO_INT(id)); } static void sway_ipc_set_workspace ( workspace_t *ws ) { sway_ipc_command("workspace '%s'",ws->name); } static gint sway_ipc_comp_workspace ( gpointer name, gpointer id ) { return pager_workspace_id_from_name(name) - id; } static struct wintree_api sway_wintree_api = { .minimize = sway_ipc_minimize, .unminimize = sway_ipc_unminimize, .maximize = sway_ipc_maximize, .unmaximize = sway_ipc_unmaximize, .close = sway_ipc_close, .focus = sway_ipc_focus, .free_workspace = g_free, .comp_workspace = sway_ipc_comp_workspace, }; static guint sway_ipc_get_geom ( workspace_t *ws, GdkRectangle **wins, GdkRectangle *space, gint *focus ) { gint32 etype; struct json_object *obj = NULL; struct json_object *iter,*fiter,*arr; gint i,j,n = 0; obj = sway_ipc_request("",1,&etype); *wins = NULL; *focus = -1; if(obj && json_object_is_type(obj,json_type_array)) for(i=0;iname,json_string_by_name(iter,"name"))) { *space = sway_ipc_parse_rect(iter); json_object_object_get_ex(iter,"floating_nodes",&arr); if(arr && json_object_is_type(arr,json_type_array)) { n = json_object_array_length(arr); *wins = g_malloc0(n * sizeof(GdkRectangle)); for(j=0;j #include #include #include "sfwbar.h" #include "switcheritem.h" #include "switcher.h" #include "wintree.h" #include "pager.h" #include "config.h" #include "bar.h" G_DEFINE_TYPE_WITH_CODE (Switcher, switcher, BASE_WIDGET_TYPE, G_ADD_PRIVATE (Switcher)); static GtkWidget *switcher; static GtkWidget *grid; static gint interval; static gchar hstate; static gint counter; static gint title_width = -1; static window_t *focus; static GtkWidget *switcher_get_child ( GtkWidget *self ) { SwitcherPrivate *priv; g_return_val_if_fail(IS_SWITCHER(self),NULL); priv = switcher_get_instance_private(SWITCHER(self)); return priv->switcher; } static void switcher_class_init ( SwitcherClass *kclass ) { BASE_WIDGET_CLASS(kclass)->get_child = switcher_get_child; } static void switcher_init ( Switcher *self ) { } gboolean switcher_state ( void ) { return switcher != NULL; } GtkWidget *switcher_new ( void ) { GtkWidget *self; SwitcherPrivate *priv; if(grid) return grid; self = GTK_WIDGET(g_object_new(switcher_get_type(), NULL)); priv = switcher_get_instance_private(SWITCHER(self)); priv->switcher = flow_grid_new(FALSE); gtk_container_add(GTK_CONTAINER(self),priv->switcher); if(!switcher) { grid = self; gtk_widget_set_name(base_widget_get_child(grid), "switcher"); switcher = gtk_window_new(GTK_WINDOW_TOPLEVEL); gtk_layer_init_for_window (GTK_WINDOW(switcher)); gtk_layer_set_layer(GTK_WINDOW(switcher),GTK_LAYER_SHELL_LAYER_OVERLAY); gtk_widget_set_name(switcher, "switcher"); gtk_container_add(GTK_CONTAINER(switcher),grid); } hstate = 's'; return self; } void switcher_populate ( void ) { GList *iter; if(!grid) return; interval = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(grid),"interval")); title_width = GPOINTER_TO_INT( g_object_get_data(G_OBJECT(grid),"title_width")); if(!title_width) title_width = -1; for(iter=wintree_get_list(); iter; iter=g_list_next(iter)) switcher_window_init(iter->data); } gboolean switcher_is_focused ( gpointer uid ) { return focus?uid==focus->uid:FALSE; } gboolean switcher_check ( GtkWidget *switcher, window_t *win ) { switch(switcher_get_filter(switcher)) { case G_TOKEN_OUTPUT: return (!win->outputs || g_list_find_custom(win->outputs, bar_get_output(base_widget_get_child(switcher)), (GCompareFunc)g_strcmp0)); case G_TOKEN_WORKSPACE: return (!win->workspace || wintree_comp_workspace(win->workspace, pager_get_focused())); } return !wintree_is_filtered(win); } gboolean switcher_event ( gpointer dir ) { GList *iter, *list = NULL, *flink = NULL; if(counter<1 || !focus) focus = wintree_from_id(wintree_get_focus()); counter = interval; for (iter = wintree_get_list(); iter; iter = g_list_next(iter) ) if(switcher_check(grid, iter->data)) list = g_list_prepend(list, iter->data); list = g_list_reverse(list); flink = g_list_find(list, focus); if(!dir) flink = g_list_next(flink)?g_list_next(flink):list; else flink = g_list_previous(flink)?g_list_previous(flink):g_list_last(list); if(flink) focus = flink->data; g_list_free(list); return TRUE; } void switcher_window_delete ( window_t *win ) { if(grid) flow_grid_delete_child(grid,win); if(win->switcher) gtk_widget_destroy(win->switcher); } void switcher_window_init ( window_t *win) { if(!grid) return; flow_grid_add_child(grid,switcher_item_new(win,grid)); } void switcher_update ( void ) { GList *item; if(!switcher) return; if(counter <= 0) return; counter--; if(counter > 0) { for (item = wintree_get_list(); item; item = g_list_next(item) ) flow_item_invalidate(flow_grid_find_child(grid,item->data)); flow_grid_update(grid); css_widget_cascade(switcher,NULL); } else { gtk_widget_hide(switcher); wintree_focus(focus->uid); } } void switcher_set_filter ( GtkWidget *self, gint filter ) { SwitcherPrivate *priv; g_return_if_fail(IS_SWITCHER(self)); priv = switcher_get_instance_private(SWITCHER(self)); priv->filter = filter; } gint switcher_get_filter ( GtkWidget *self ) { SwitcherPrivate *priv; g_return_val_if_fail(IS_SWITCHER(self),0); priv = switcher_get_instance_private(SWITCHER(self)); return priv->filter; } void switcher_invalidate ( window_t *win ) { if(grid) flow_item_invalidate(flow_grid_find_child(grid, win)); } sfwbar-1.0~beta13/src/switcher.h000066400000000000000000000025261450657570000166420ustar00rootroot00000000000000#ifndef __SWITCHER_H__ #define __SWITCHER_H__ #include "basewidget.h" #include "flowgrid.h" #define SWITCHER_TYPE (switcher_get_type()) #define SWITCHER(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), SWITCHER_TYPE, Switcher)) #define SWITCHER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), SWITCHER_TYPE, SwitcherClass)) #define IS_SWITCHER(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), SWITCHER_TYPE)) #define IS_SWITCHERCLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), SWITCHER_TYPE)) typedef struct _Switcher Switcher; typedef struct _SwitcherClass SwitcherClass; struct _Switcher { BaseWidget item; }; struct _SwitcherClass { BaseWidgetClass parent_class; }; typedef struct _SwitcherPrivate SwitcherPrivate; struct _SwitcherPrivate { GtkWidget *switcher; gint filter; }; GType switcher_get_type ( void ); void switcher_window_delete ( window_t *win ); GtkWidget *switcher_new(); gboolean switcher_state ( void ); gboolean switcher_event ( gpointer ); void switcher_invalidate ( window_t *win ); void switcher_update ( void ); void switcher_window_init ( window_t *win); void switcher_populate ( void ); void switcher_set_filter ( GtkWidget *self, gint filter ); gint switcher_get_filter ( GtkWidget *self ); gboolean switcher_check ( GtkWidget *switcher, window_t *win ); gboolean switcher_is_focused ( gpointer uid ); #endif sfwbar-1.0~beta13/src/switcheritem.c000066400000000000000000000105271450657570000175140ustar00rootroot00000000000000/* This entire file is licensed under GNU General Public License v3.0 * * Copyright 2022- sfwbar maintainers */ #include "sfwbar.h" #include "switcheritem.h" #include "flowgrid.h" #include "scaleimage.h" #include "pager.h" #include "switcher.h" G_DEFINE_TYPE_WITH_CODE (SwitcherItem, switcher_item, FLOW_ITEM_TYPE, G_ADD_PRIVATE (SwitcherItem)); static gboolean switcher_item_check ( GtkWidget *self ) { SwitcherItemPrivate *priv; g_return_val_if_fail(IS_SWITCHER_ITEM(self),FALSE); priv = switcher_item_get_instance_private(SWITCHER_ITEM(self)); return switcher_check(priv->switcher,priv->win); } void switcher_item_update ( GtkWidget *self ) { SwitcherItemPrivate *priv; g_return_if_fail(IS_SWITCHER_ITEM(self)); priv = switcher_item_get_instance_private(SWITCHER_ITEM(self)); if(!priv->invalid) return; if(priv->label) if(g_strcmp0(gtk_label_get_text(GTK_LABEL(priv->label)),priv->win->title)) gtk_label_set_text(GTK_LABEL(priv->label), priv->win->title); if(priv->icon) scale_image_set_image(priv->icon,priv->win->appid,NULL); if ( switcher_is_focused(((window_t *)flow_item_get_parent(self))->uid) ) gtk_widget_set_name(gtk_bin_get_child(GTK_BIN(self)), "switcher_active"); else gtk_widget_set_name(gtk_bin_get_child(GTK_BIN(self)), "switcher_normal"); gtk_widget_unset_state_flags(gtk_bin_get_child(GTK_BIN(self)), GTK_STATE_FLAG_PRELIGHT); flow_item_set_active(self, switcher_item_check(self)); priv->invalid = FALSE; } window_t *switcher_item_get_window ( GtkWidget *self ) { SwitcherItemPrivate *priv; g_return_val_if_fail(IS_SWITCHER_ITEM(self),NULL); priv = switcher_item_get_instance_private(SWITCHER_ITEM(self)); return priv->win; } static void switcher_item_invalidate ( GtkWidget *self ) { SwitcherItemPrivate *priv; g_return_if_fail(IS_SWITCHER_ITEM(self)); priv = switcher_item_get_instance_private(SWITCHER_ITEM(self)); flow_grid_invalidate(priv->switcher); priv->invalid = TRUE; } static gint switcher_item_compare ( GtkWidget *a, GtkWidget *b, GtkWidget *parent ) { SwitcherItemPrivate *p1,*p2; g_return_val_if_fail(IS_SWITCHER_ITEM(a),0); g_return_val_if_fail(IS_SWITCHER_ITEM(b),0); p1 = switcher_item_get_instance_private(SWITCHER_ITEM(a)); p2 = switcher_item_get_instance_private(SWITCHER_ITEM(b)); if(g_list_find(g_list_find(wintree_get_list(), p1->win), p2->win)) return -1; else return 1; } static void switcher_item_class_init ( SwitcherItemClass *kclass ) { FLOW_ITEM_CLASS(kclass)->update = switcher_item_update; FLOW_ITEM_CLASS(kclass)->compare = switcher_item_compare; FLOW_ITEM_CLASS(kclass)->invalidate = switcher_item_invalidate; FLOW_ITEM_CLASS(kclass)->get_parent = (void * (*)(GtkWidget *))switcher_item_get_window; } static void switcher_item_init ( SwitcherItem *cgrid ) { } GtkWidget *switcher_item_new( window_t *win, GtkWidget *switcher ) { GtkWidget *self,*grid; SwitcherItemPrivate *priv; gboolean icons,labels; gint dir; gint title_width; if(!switcher) { win->switcher = NULL; return NULL; } self = GTK_WIDGET(g_object_new(switcher_item_get_type(), NULL)); priv = switcher_item_get_instance_private(SWITCHER_ITEM(self)); priv->win = win; priv->switcher = switcher; icons = GPOINTER_TO_INT( g_object_get_data(G_OBJECT(switcher),"icons")); labels = GPOINTER_TO_INT( g_object_get_data(G_OBJECT(switcher),"labels")); title_width = GPOINTER_TO_INT( g_object_get_data(G_OBJECT(switcher),"title_width")); if(!title_width) title_width = -1; if(!icons) labels = TRUE; win->switcher = self; grid = gtk_grid_new(); gtk_container_add(GTK_CONTAINER(self),grid); gtk_widget_set_name(grid, "switcher_normal"); gtk_widget_style_get(grid,"direction",&dir,NULL); g_object_ref(G_OBJECT(self)); if(icons) { priv->icon = scale_image_new(); scale_image_set_image(priv->icon,win->appid,NULL); gtk_grid_attach_next_to(GTK_GRID(grid),priv->icon,NULL,dir,1,1); } else priv->icon = NULL; if(labels) { priv->label = gtk_label_new(win->title); gtk_label_set_ellipsize (GTK_LABEL(priv->label),PANGO_ELLIPSIZE_END); gtk_label_set_max_width_chars(GTK_LABEL(priv->label),title_width); gtk_grid_attach_next_to(GTK_GRID(grid),priv->label, priv->icon,dir,1,1); } else priv->label = NULL; flow_item_invalidate(self); return self; } sfwbar-1.0~beta13/src/switcheritem.h000066400000000000000000000021411450657570000175120ustar00rootroot00000000000000#ifndef __SWITCHERITEM_H__ #define __SWITCHERITEM_H__ #include "sfwbar.h" #include "flowitem.h" #include "wintree.h" #define SWITCHER_ITEM_TYPE (switcher_item_get_type()) #define SWITCHER_ITEM(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), SWITCHER_ITEM_TYPE, SwitcherItem)) #define SWITCHER_ITEM_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), SWITCHER_ITEM_TYPE, SwitcherItemClass)) #define IS_SWITCHER_ITEM(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), SWITCHER_ITEM_TYPE)) #define IS_SWITCHER_ITEMCLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), SWITCHER_ITEM_TYPE)) typedef struct _SwitcherItem SwitcherItem; typedef struct _SwitcherItemClass SwitcherItemClass; struct _SwitcherItem { FlowItem item; }; struct _SwitcherItemClass { FlowItemClass parent_class; }; typedef struct _SwitcherItemPrivate SwitcherItemPrivate; struct _SwitcherItemPrivate { GtkWidget *icon; GtkWidget *label; GtkWidget *grid; GtkWidget *switcher; window_t *win; gboolean invalid; }; GType switcher_item_get_type ( void ); GtkWidget *switcher_item_new( window_t *win, GtkWidget *switcher ); #endif sfwbar-1.0~beta13/src/taskbar.c000066400000000000000000000147021450657570000164330ustar00rootroot00000000000000/* This entire file is licensed under GNU General Public License v3.0 * * Copyright 2020- sfwbar maintainers */ #include "sfwbar.h" #include "flowgrid.h" #include "taskbaritem.h" #include "taskbargroup.h" #include "taskbar.h" #include "wintree.h" #include "config.h" G_DEFINE_TYPE_WITH_CODE (Taskbar, taskbar, BASE_WIDGET_TYPE, G_ADD_PRIVATE (Taskbar)); static GList *taskbars; static GtkWidget *taskbar_get_child ( GtkWidget *self ) { TaskbarPrivate *priv; g_return_val_if_fail(IS_TASKBAR(self),NULL); priv = taskbar_get_instance_private(TASKBAR(self)); return priv->taskbar; } static GtkWidget *taskbar_mirror ( GtkWidget *src ) { GtkWidget *self; TaskbarPrivate *dpriv, *spriv; g_return_val_if_fail(IS_TASKBAR(src),NULL); spriv = taskbar_get_instance_private(TASKBAR(src)); self = taskbar_new(TRUE); dpriv = taskbar_get_instance_private(TASKBAR(self)); dpriv->filter = spriv->filter; dpriv->floating_filter = spriv->floating_filter; g_object_set_data(G_OBJECT(self),"title_width", g_object_get_data(G_OBJECT(src),"title_width")); g_object_set_data(G_OBJECT(self),"group", g_object_get_data(G_OBJECT(src),"group")); g_object_set_data(G_OBJECT(self),"g_cols", g_object_get_data(G_OBJECT(src),"g_cols")); g_object_set_data(G_OBJECT(self),"g_rows", g_object_get_data(G_OBJECT(src),"g_rows")); g_object_set_data(G_OBJECT(self),"g_icons", g_object_get_data(G_OBJECT(src),"g_icons")); g_object_set_data(G_OBJECT(self),"g_labels", g_object_get_data(G_OBJECT(src),"g_labels")); g_object_set_data_full(G_OBJECT(self),"g_css", g_strdup(g_object_get_data(G_OBJECT(src),"g_css")),g_free); g_object_set_data_full(G_OBJECT(self),"g_style", g_strdup(g_object_get_data(G_OBJECT(src),"g_style")),g_free); g_object_set_data(G_OBJECT(self),"g_title_width", g_object_get_data(G_OBJECT(src),"g_title_width")); g_object_set_data(G_OBJECT(self),"g_sort", g_object_get_data(G_OBJECT(src),"g_sort")); g_object_set_data(G_OBJECT(self),"title_width", g_object_get_data(G_OBJECT(src),"title_width")); flow_grid_copy_properties(self, src); base_widget_copy_properties(self,src); taskbar_populate(); return self; } static void taskbar_destroy ( GtkWidget *self ) { taskbars = g_list_remove(taskbars,self); GTK_WIDGET_CLASS(taskbar_parent_class)->destroy(self); } static void taskbar_class_init ( TaskbarClass *kclass ) { BASE_WIDGET_CLASS(kclass)->get_child = taskbar_get_child; BASE_WIDGET_CLASS(kclass)->mirror = taskbar_mirror; BASE_WIDGET_CLASS(kclass)->action_exec = NULL; GTK_WIDGET_CLASS(kclass)->destroy = taskbar_destroy; } static void taskbar_init ( Taskbar *self ) { } GtkWidget *taskbar_new ( gboolean toplevel ) { GtkWidget *self; TaskbarPrivate *priv; self = GTK_WIDGET(g_object_new(taskbar_get_type(), NULL)); priv = taskbar_get_instance_private(TASKBAR(self)); priv->taskbar = flow_grid_new(TRUE); gtk_container_add(GTK_CONTAINER(self),priv->taskbar); priv->toplevel = toplevel; taskbars = g_list_prepend(taskbars,self); flow_grid_invalidate(priv->taskbar); return self; } gboolean taskbar_is_toplevel ( GtkWidget *self ) { TaskbarPrivate *priv; g_return_val_if_fail(IS_TASKBAR(self),FALSE); priv = taskbar_get_instance_private(TASKBAR(self)); return priv->toplevel; } void taskbar_set_filter ( GtkWidget *self, gint filter ) { TaskbarPrivate *priv; g_return_if_fail(IS_TASKBAR(self)); priv = taskbar_get_instance_private(TASKBAR(self)); if(filter == G_TOKEN_FLOATING) priv->floating_filter = TRUE; else priv->filter = filter; } gint taskbar_get_filter ( GtkWidget *self, gboolean *floating ) { TaskbarPrivate *priv; g_return_val_if_fail(IS_TASKBAR(self),0); priv = taskbar_get_instance_private(TASKBAR(self)); *floating = priv->floating_filter; return priv->filter; } void taskbar_invalidate_all ( window_t *win, gboolean filter ) { GList *iter; gboolean floating; if(!win) return; for(iter=taskbars; iter; iter=g_list_next(iter)) if(!filter || taskbar_get_filter(iter->data,&floating) || floating) { if(taskbar_is_toplevel(iter->data) && win->appid && g_object_get_data(G_OBJECT(iter->data),"group")) flow_item_invalidate(flow_grid_find_child(iter->data,win->appid)); else flow_item_invalidate(flow_grid_find_child(iter->data,win)); } } void taskbar_invalidate_conditional ( void ) { GList *iter; for(iter=wintree_get_list();iter;iter=g_list_next(iter)) taskbar_invalidate_all(iter->data,TRUE); } void taskbar_init_item ( window_t *win ) { GList *iter; GtkWidget *taskbar; for(iter=taskbars; iter; iter=g_list_next(iter)) if(iter->data && taskbar_is_toplevel(iter->data)) { if(g_object_get_data(G_OBJECT(iter->data),"group")) taskbar = taskbar_group_new(win->appid,iter->data); else taskbar = iter->data; taskbar_item_new(win,taskbar); } } void taskbar_populate ( void ) { GList *iter; if(!taskbars) return; for(iter=wintree_get_list(); iter; iter=g_list_next(iter)) taskbar_init_item (iter->data); } void taskbar_reparent_item ( window_t *win, const gchar *new_appid ) { GList *iter; GtkWidget *taskbar, *group; for(iter=taskbars; iter; iter=g_list_next(iter)) if(iter->data && taskbar_is_toplevel(iter->data) && g_object_get_data(G_OBJECT(iter->data),"group")) { taskbar = iter->data; group = taskbar_group_new(win->appid,taskbar); flow_grid_delete_child(group,win); if(!flow_grid_n_children(group)) { taskbars = g_list_remove(taskbars,group); flow_grid_delete_child(taskbar,win->appid); } group = taskbar_group_new(new_appid,taskbar); taskbar_item_new(win,group); } } void taskbar_destroy_item ( window_t *win ) { GList *iter; GtkWidget *taskbar, *tgroup; for(iter=taskbars; iter; iter=g_list_next(iter)) if(iter->data && taskbar_is_toplevel(iter->data)) { taskbar = iter->data; if(g_object_get_data(G_OBJECT(taskbar),"group")) { tgroup = taskbar_group_new(win->appid,taskbar); flow_grid_delete_child(tgroup,win); if(!flow_grid_n_children(tgroup)) { taskbars = g_list_remove(taskbars,tgroup); flow_grid_delete_child(taskbar, win->appid); } } else flow_grid_delete_child(taskbar,win); } } void taskbar_update_all ( void ) { GList *iter; for(iter=taskbars; iter; iter=g_list_next(iter)) flow_grid_update(iter->data); } sfwbar-1.0~beta13/src/taskbar.h000066400000000000000000000024721450657570000164410ustar00rootroot00000000000000#ifndef __TASKBAR_H__ #define __TASKBAR_H__ #include "basewidget.h" #define TASKBAR_TYPE (taskbar_get_type()) #define TASKBAR(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), TASKBAR_TYPE, Taskbar)) #define TASKBAR_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), TASKBAR_TYPE, TaskbarClass)) #define IS_TASKBAR(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), TASKBAR_TYPE)) #define IS_TASKBARCLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), TASKBAR_TYPE)) typedef struct _Taskbar Taskbar; typedef struct _TaskbarClass TaskbarClass; struct _Taskbar { BaseWidget item; }; struct _TaskbarClass { BaseWidgetClass parent_class; }; typedef struct _TaskbarPrivate TaskbarPrivate; struct _TaskbarPrivate { GtkWidget *taskbar; gboolean toplevel; gboolean floating_filter; gint filter; }; GType taskbar_get_type ( void ); GtkWidget *taskbar_new( gboolean ); void taskbar_populate ( void ); void taskbar_invalidate_all ( window_t *win, gboolean filter ); void taskbar_update_all ( void ); void taskbar_init_item ( window_t *win ); void taskbar_destroy_item ( window_t *win ); void taskbar_reparent_item ( window_t *win, const gchar *new_appid ); void taskbar_set_filter ( GtkWidget *self, gint filter ); gint taskbar_get_filter ( GtkWidget *self, gboolean * ); void taskbar_invalidate_conditional ( void ); #endif sfwbar-1.0~beta13/src/taskbargroup.c000066400000000000000000000244101450657570000175050ustar00rootroot00000000000000/* This entire file is licensed under GNU General Public License v3.0 * * Copyright 2022- sfwbar maintainers */ #include "sfwbar.h" #include "flowgrid.h" #include "taskbargroup.h" #include "taskbar.h" #include "scaleimage.h" #include "action.h" #include "bar.h" #include "wintree.h" #include "menu.h" #include "popup.h" #include G_DEFINE_TYPE_WITH_CODE (TaskbarGroup, taskbar_group, FLOW_ITEM_TYPE, G_ADD_PRIVATE(TaskbarGroup)); static void taskbar_group_add_hold ( GtkWidget *popover, GtkWidget *hold ) { GList **holds; holds = g_object_get_data(G_OBJECT(popover),"holds"); if(holds && !g_list_find(*holds,hold)) *holds = g_list_prepend(*holds,hold); } static void taskbar_group_remove_hold ( GtkWidget *popover, GtkWidget *hold ) { GList **holds; holds = g_object_get_data(G_OBJECT(popover),"holds"); if(holds) *holds = g_list_remove(*holds,hold); } static gboolean taskbar_group_enter_cb ( GtkWidget *widget, GdkEventCrossing *event, gpointer self ) { TaskbarGroupPrivate *priv; g_return_val_if_fail(IS_TASKBAR_GROUP(self),FALSE); priv = taskbar_group_get_instance_private(TASKBAR_GROUP(self)); if(priv->single) return FALSE; if(gtk_widget_is_visible(priv->popover)) { taskbar_group_add_hold(priv->popover,widget); return FALSE; } g_list_free(priv->holds); taskbar_group_add_hold(priv->popover,widget); flow_grid_update(priv->tgroup); popup_show(priv->button, priv->popover, (GdkEvent *)event); return FALSE; } static gboolean taskbar_group_timeout_cb ( GtkWidget *popover ) { GList **holds; holds = g_object_get_data(G_OBJECT(popover),"holds"); if(!holds || !*holds) gtk_widget_hide(popover); return FALSE; } static gboolean taskbar_group_leave_cb ( GtkWidget *widget, GdkEventCrossing *event, gpointer self ) { TaskbarGroupPrivate *priv; g_return_val_if_fail(IS_TASKBAR_GROUP(self),FALSE); priv = taskbar_group_get_instance_private(TASKBAR_GROUP(self)); taskbar_group_remove_hold(priv->popover, widget); if(!priv->single) g_timeout_add(10,(GSourceFunc)taskbar_group_timeout_cb, priv->popover); return FALSE; } static void taskbar_group_destroy ( GtkWidget *self ) { TaskbarGroupPrivate *priv; g_return_if_fail(IS_TASKBAR_GROUP(self)); priv = taskbar_group_get_instance_private(TASKBAR_GROUP(self)); gtk_widget_destroy(priv->popover); priv->popover = NULL; GTK_WIDGET_CLASS(taskbar_group_parent_class)->destroy(self); } static gchar *taskbar_group_get_appid ( GtkWidget *self ) { TaskbarGroupPrivate *priv; g_return_val_if_fail(IS_TASKBAR_GROUP(self),NULL); priv = taskbar_group_get_instance_private(TASKBAR_GROUP(self)); return priv->appid; } static void taskbar_group_update ( GtkWidget *self ) { TaskbarGroupPrivate *priv; GList *children, *iter; g_return_if_fail(IS_TASKBAR_GROUP(self)); priv = taskbar_group_get_instance_private(TASKBAR_GROUP(self)); if(!priv->invalid) return; if(priv->icon) scale_image_set_image(priv->icon,priv->appid,NULL); if(priv->label) if(g_strcmp0(gtk_label_get_text(GTK_LABEL(priv->label)),priv->appid)) gtk_label_set_text(GTK_LABEL(priv->label),priv->appid); if (flow_grid_find_child(priv->tgroup, wintree_from_id(wintree_get_focus()))) gtk_widget_set_name(gtk_bin_get_child(GTK_BIN(self)), "taskbar_group_active"); else gtk_widget_set_name(gtk_bin_get_child(GTK_BIN(self)), "taskbar_group_normal"); gtk_widget_unset_state_flags(gtk_bin_get_child(GTK_BIN(self)), GTK_STATE_FLAG_PRELIGHT); flow_grid_update(priv->tgroup); children = gtk_container_get_children(GTK_CONTAINER( gtk_bin_get_child(GTK_BIN(priv->tgroup)))); flow_item_set_active(self, g_list_length(children)>0 ); priv->single = !!(g_list_length(children) == 1); g_list_free(children); for(iter=priv->holds;iter;iter=g_list_next(iter)) { if(GTK_IS_WINDOW(iter->data)) gtk_widget_hide(iter->data); if(GTK_IS_MENU(iter->data)) { gtk_menu_popdown(iter->data); iter = priv->holds; } } g_list_free(priv->holds); priv->holds = NULL; gtk_widget_hide(priv->popover); priv->invalid = FALSE; } static gint taskbar_group_compare ( GtkWidget *a, GtkWidget *b, GtkWidget *parent ) { TaskbarGroupPrivate *p1,*p2; g_return_val_if_fail(IS_TASKBAR_GROUP(a),0); g_return_val_if_fail(IS_TASKBAR_GROUP(b),0); p1 = taskbar_group_get_instance_private(TASKBAR_GROUP(a)); p2 = taskbar_group_get_instance_private(TASKBAR_GROUP(b)); return g_strcmp0(p1->appid,p2->appid); } static gboolean taskbar_group_action_exec ( GtkWidget *self, gint slot, GdkEvent *ev ) { TaskbarGroupPrivate *priv; GList *children; g_return_val_if_fail(IS_TASKBAR_GROUP(self),FALSE); priv = taskbar_group_get_instance_private(TASKBAR_GROUP(self)); children = gtk_container_get_children(GTK_CONTAINER( gtk_bin_get_child(GTK_BIN(priv->tgroup)))); if(g_list_length(children) == 1) base_widget_action_exec(children->data, slot, ev); g_list_free(children); return TRUE; } static void taskbar_group_invalidate ( GtkWidget *self ) { TaskbarGroupPrivate *priv; if(!self) return; g_return_if_fail(IS_TASKBAR_GROUP(self)); priv = taskbar_group_get_instance_private(TASKBAR_GROUP(self)); flow_grid_invalidate(priv->taskbar); priv->invalid = TRUE; } static void taskbar_group_class_init ( TaskbarGroupClass *kclass ) { GTK_WIDGET_CLASS(kclass)->destroy = taskbar_group_destroy; BASE_WIDGET_CLASS(kclass)->action_exec = taskbar_group_action_exec; FLOW_ITEM_CLASS(kclass)->update = taskbar_group_update; FLOW_ITEM_CLASS(kclass)->invalidate = taskbar_group_invalidate; FLOW_ITEM_CLASS(kclass)->comp_parent = (GCompareFunc)g_strcmp0; FLOW_ITEM_CLASS(kclass)->get_parent = (void * (*)(GtkWidget *))taskbar_group_get_appid; FLOW_ITEM_CLASS(kclass)->compare = taskbar_group_compare; } static void taskbar_group_init ( TaskbarGroup *self ) { } GtkWidget *taskbar_group_new( const gchar *appid, GtkWidget *taskbar ) { GtkWidget *self; TaskbarGroupPrivate *priv; gint dir; gboolean icons, labels; gint title_width; GtkWidget *box; g_return_val_if_fail(IS_TASKBAR(taskbar),NULL); self = flow_grid_find_child(taskbar, appid); if(!self) self = GTK_WIDGET(g_object_new(taskbar_group_get_type(), NULL)); priv = taskbar_group_get_instance_private(TASKBAR_GROUP(self)); if(priv->tgroup) return priv->tgroup; icons = GPOINTER_TO_INT( g_object_get_data(G_OBJECT(taskbar), "icons")); labels = GPOINTER_TO_INT( g_object_get_data(G_OBJECT(taskbar), "labels")); title_width = GPOINTER_TO_INT( g_object_get_data(G_OBJECT(taskbar), "title_width")); if(!title_width) title_width = -1; if(!icons) labels = TRUE; priv->taskbar = taskbar; priv->tgroup = taskbar_new(FALSE); g_object_set_data(G_OBJECT(priv->tgroup), "parent_taskbar", taskbar); priv->appid = g_strdup(appid); priv->button = gtk_button_new(); gtk_container_add(GTK_CONTAINER(self), priv->button); gtk_widget_set_name(priv->button, "taskbar_group_normal"); gtk_widget_style_get(priv->button, "direction", &dir, NULL); box = gtk_grid_new(); gtk_container_add(GTK_CONTAINER(priv->button), box); if(icons) { priv->icon = scale_image_new(); scale_image_set_image(priv->icon,appid,NULL); gtk_grid_attach_next_to(GTK_GRID(box),priv->icon,NULL,dir,1,1); } else priv->icon = NULL; if(labels) { priv->label = gtk_label_new(appid); gtk_label_set_ellipsize (GTK_LABEL(priv->label), PANGO_ELLIPSIZE_END); gtk_label_set_max_width_chars(GTK_LABEL(priv->label), title_width); gtk_grid_attach_next_to(GTK_GRID(box), priv->label, priv->icon, dir, 1, 1); } else priv->label = NULL; priv->popover = gtk_window_new(GTK_WINDOW_POPUP); gtk_widget_set_name(priv->button, "taskbar_group"); g_object_ref(G_OBJECT(priv->popover)); gtk_container_add(GTK_CONTAINER(priv->popover), priv->tgroup); css_widget_apply(priv->tgroup, g_strdup( g_object_get_data(G_OBJECT(taskbar), "g_css"))); base_widget_set_style(priv->tgroup,g_strdup( g_object_get_data(G_OBJECT(taskbar), "g_style"))); gtk_widget_show(priv->tgroup); gtk_widget_add_events(GTK_WIDGET(self),GDK_ENTER_NOTIFY_MASK | GDK_LEAVE_NOTIFY_MASK | GDK_FOCUS_CHANGE_MASK | GDK_SCROLL_MASK); g_signal_connect(self, "enter-notify-event", G_CALLBACK(taskbar_group_enter_cb), self); g_signal_connect(priv->button, "enter-notify-event", G_CALLBACK(taskbar_group_enter_cb), self); g_signal_connect(priv->popover, "enter-notify-event", G_CALLBACK(taskbar_group_enter_cb), self); g_signal_connect(self, "leave-notify-event", G_CALLBACK(taskbar_group_leave_cb), self); g_signal_connect(priv->button, "leave-notify-event", G_CALLBACK(taskbar_group_leave_cb), self); g_signal_connect(priv->popover, "leave-notify-event", G_CALLBACK(taskbar_group_leave_cb), self); if(g_object_get_data(G_OBJECT(taskbar), "g_cols")) flow_grid_set_cols(base_widget_get_child(priv->tgroup), GPOINTER_TO_INT( g_object_get_data(G_OBJECT(taskbar), "g_cols"))); if(g_object_get_data(G_OBJECT(taskbar), "g_rows")) flow_grid_set_rows(base_widget_get_child(priv->tgroup), GPOINTER_TO_INT( g_object_get_data(G_OBJECT(taskbar), "g_rows"))); g_object_set_data(G_OBJECT(priv->tgroup), "labels", g_object_get_data(G_OBJECT(taskbar), "g_labels")); g_object_set_data(G_OBJECT(priv->tgroup), "icons", g_object_get_data(G_OBJECT(taskbar), "g_icons")); g_object_set_data(G_OBJECT(priv->tgroup), "title_width", g_object_get_data(G_OBJECT(taskbar), "g_title_width")); flow_grid_set_sort(base_widget_get_child(priv->tgroup), GPOINTER_TO_INT(g_object_get_data(G_OBJECT(taskbar), "g_sort"))); g_object_set_data(G_OBJECT(priv->popover), "holds", &priv->holds); base_widget_copy_actions(priv->tgroup, taskbar); g_object_ref(G_OBJECT(self)); flow_grid_add_child(taskbar, self); flow_item_invalidate(self); return priv->tgroup; } gboolean taskbar_group_child_cb ( GtkWidget *child, GtkWidget *popover ) { taskbar_group_remove_hold(popover, child); g_timeout_add(10, (GSourceFunc)taskbar_group_timeout_cb, popover); return FALSE; } void taskbar_group_pop_child ( GtkWidget *popover, GtkWidget *child ) { taskbar_group_add_hold(popover, child); g_signal_connect(G_OBJECT(child), "unmap", G_CALLBACK(taskbar_group_child_cb), popover); } sfwbar-1.0~beta13/src/taskbargroup.h000066400000000000000000000023761450657570000175210ustar00rootroot00000000000000#ifndef __TASKBARGROUPITEM_H__ #define __TASKBARGROUPITEM_H__ #include "sfwbar.h" #include "flowitem.h" #include "action.h" #define TASKBAR_GROUP_TYPE (taskbar_group_get_type()) #define TASKBAR_GROUP(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), TASKBAR_GROUP_TYPE, TaskbarGroup)) #define TASKBAR_GROUP_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), TASKBAR_GROUP_TYPE, TaskbarGroupClass)) #define IS_TASKBAR_GROUP(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), TASKBAR_GROUP_TYPE)) #define IS_TASKBAR_GROUPCLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), TASKBAR_GROUP_TYPE)) typedef struct _TaskbarGroup TaskbarGroup; typedef struct _TaskbarGroupClass TaskbarGroupClass; struct _TaskbarGroup { FlowItem item; }; struct _TaskbarGroupClass { FlowItemClass parent_class; }; typedef struct _TaskbarGroupPrivate TaskbarGroupPrivate; struct _TaskbarGroupPrivate { GtkWidget *icon; GtkWidget *label; GtkWidget *button; GtkWidget *taskbar; GtkWidget *tgroup; GtkWidget *popover; gchar *appid; gboolean invalid; gboolean single; GList *holds; }; GType taskbar_group_get_type ( void ); GtkWidget *taskbar_group_new( const gchar *appid, GtkWidget *taskbar ); void taskbar_group_pop_child ( GtkWidget *self, GtkWidget *child ); #endif sfwbar-1.0~beta13/src/taskbaritem.c000066400000000000000000000145401450657570000173120ustar00rootroot00000000000000/* This entire file is licensed under GNU General Public License v3.0 * * Copyright 2022- sfwbar maintainers */ #include "sfwbar.h" #include "flowgrid.h" #include "taskbaritem.h" #include "taskbar.h" #include "scaleimage.h" #include "action.h" #include "pager.h" #include "bar.h" #include "wintree.h" #include "config.h" G_DEFINE_TYPE_WITH_CODE (TaskbarItem, taskbar_item, FLOW_ITEM_TYPE, G_ADD_PRIVATE (TaskbarItem)); static gboolean taskbar_item_action_exec ( GtkWidget *self, gint slot, GdkEvent *ev ) { TaskbarItemPrivate *priv; GdkModifierType mods; action_t *action; g_return_val_if_fail(IS_TASKBAR_ITEM(self),FALSE); priv = taskbar_item_get_instance_private(TASKBAR_ITEM(self)); if(!base_widget_check_action_slot(priv->taskbar, slot) && slot != 1) return FALSE; mods = base_widget_get_modifiers(self); action = base_widget_get_action(priv->taskbar, slot, mods); if(!action && !mods && slot==1) { if (wintree_is_focused(priv->win->uid) && !(priv->win->state & WS_MINIMIZED)) wintree_minimize(priv->win->uid); else wintree_focus(priv->win->uid); taskbar_invalidate_all(priv->win, FALSE); } else action_exec(self, action, ev, priv->win, NULL); return TRUE; } window_t *taskbar_item_get_window ( GtkWidget *self ) { TaskbarItemPrivate *priv; g_return_val_if_fail(IS_TASKBAR_ITEM(self),NULL); priv = taskbar_item_get_instance_private(TASKBAR_ITEM(self)); return priv->win; } static gboolean taskbar_item_check ( GtkWidget *self ) { TaskbarItemPrivate *priv; GtkWidget *taskbar; gboolean floating, result; g_return_val_if_fail(IS_TASKBAR_ITEM(self),FALSE); priv = taskbar_item_get_instance_private(TASKBAR_ITEM(self)); taskbar = g_object_get_data(G_OBJECT(priv->taskbar),"parent_taskbar"); if(!taskbar) taskbar = priv->taskbar; result = TRUE; switch(taskbar_get_filter(taskbar,&floating)) { case G_TOKEN_OUTPUT: result = (!priv->win->outputs || g_list_find_custom(priv->win->outputs, bar_get_output(base_widget_get_child(taskbar)), (GCompareFunc)g_strcmp0)); break; case G_TOKEN_WORKSPACE: result = (!priv->win->workspace || !wintree_comp_workspace( priv->win->workspace,pager_workspace_get_active(taskbar))); break; } if(floating) result = result & priv->win->floating; if(result) result = !wintree_is_filtered(priv->win); return result; } static void taskbar_item_update ( GtkWidget *self ) { TaskbarItemPrivate *priv; gchar *appid; g_return_if_fail(IS_TASKBAR_ITEM(self)); priv = taskbar_item_get_instance_private(TASKBAR_ITEM(self)); if(!priv->invalid) return; if(priv->label) if(g_strcmp0(gtk_label_get_text(GTK_LABEL(priv->label)),priv->win->title)) gtk_label_set_text(GTK_LABEL(priv->label), priv->win->title); if(priv->icon) { if(priv->win->appid && *(priv->win->appid)) appid = priv->win->appid; else appid = wintree_appid_map_lookup(priv->win->title); scale_image_set_image(priv->icon,appid,NULL); } if ( wintree_is_focused(priv->win->uid) ) gtk_widget_set_name(gtk_bin_get_child(GTK_BIN(self)), "taskbar_active"); else gtk_widget_set_name(gtk_bin_get_child(GTK_BIN(self)), "taskbar_normal"); gtk_widget_unset_state_flags(gtk_bin_get_child(GTK_BIN(self)), GTK_STATE_FLAG_PRELIGHT); flow_item_set_active(self, taskbar_item_check(self)); priv->invalid = FALSE; } static gint taskbar_item_compare ( GtkWidget *a, GtkWidget *b, GtkWidget *parent ) { TaskbarItemPrivate *p1,*p2; g_return_val_if_fail(IS_TASKBAR_ITEM(a),0); g_return_val_if_fail(IS_TASKBAR_ITEM(b),0); p1 = taskbar_item_get_instance_private(TASKBAR_ITEM(a)); p2 = taskbar_item_get_instance_private(TASKBAR_ITEM(b)); return wintree_compare(p1->win,p2->win); } static void taskbar_item_invalidate ( GtkWidget *self ) { TaskbarItemPrivate *priv; if(!self) return; g_return_if_fail(IS_TASKBAR_ITEM(self)); priv = taskbar_item_get_instance_private(TASKBAR_ITEM(self)); flow_grid_invalidate(priv->taskbar); priv->invalid = TRUE; } static void taskbar_item_class_init ( TaskbarItemClass *kclass ) { BASE_WIDGET_CLASS(kclass)->action_exec = taskbar_item_action_exec; FLOW_ITEM_CLASS(kclass)->update = taskbar_item_update; FLOW_ITEM_CLASS(kclass)->invalidate = taskbar_item_invalidate; FLOW_ITEM_CLASS(kclass)->get_parent = (void * (*)(GtkWidget *))taskbar_item_get_window; FLOW_ITEM_CLASS(kclass)->compare = taskbar_item_compare; } static void taskbar_item_init ( TaskbarItem *self ) { } GtkWidget *taskbar_item_new( window_t *win, GtkWidget *taskbar ) { GtkWidget *self; TaskbarItemPrivate *priv; GtkWidget *box, *button; gint dir; gboolean icons, labels; gint title_width; g_return_val_if_fail(IS_TASKBAR(taskbar),NULL); if(flow_grid_find_child(taskbar,win)) return NULL; self = GTK_WIDGET(g_object_new(taskbar_item_get_type(), NULL)); priv = taskbar_item_get_instance_private(TASKBAR_ITEM(self)); priv->win = win; priv->taskbar = taskbar; icons = GPOINTER_TO_INT( g_object_get_data(G_OBJECT(taskbar),"icons")); labels = GPOINTER_TO_INT( g_object_get_data(G_OBJECT(taskbar),"labels")); title_width = GPOINTER_TO_INT( g_object_get_data(G_OBJECT(taskbar),"title_width")); if(!title_width) title_width = -1; if(!icons) labels = TRUE; button = gtk_button_new(); gtk_container_add(GTK_CONTAINER(self),button); gtk_widget_set_name(button, "taskbar_normal"); gtk_widget_style_get(button,"direction",&dir,NULL); box = gtk_grid_new(); gtk_container_add(GTK_CONTAINER(button),box); flow_grid_child_dnd_enable(taskbar,self,button); if(icons) { priv->icon = scale_image_new(); scale_image_set_image(priv->icon,win->appid,NULL); gtk_grid_attach_next_to(GTK_GRID(box),priv->icon,NULL,dir,1,1); } else priv->icon = NULL; if(labels) { priv->label = gtk_label_new(win->title); gtk_label_set_ellipsize (GTK_LABEL(priv->label),PANGO_ELLIPSIZE_END); gtk_label_set_max_width_chars(GTK_LABEL(priv->label),title_width); gtk_grid_attach_next_to(GTK_GRID(box),priv->label,priv->icon,dir,1,1); } else priv->label = NULL; priv->actions = g_object_get_data(G_OBJECT(taskbar),"actions"); g_object_ref(G_OBJECT(self)); flow_grid_add_child(taskbar,self); gtk_widget_add_events(self, GDK_BUTTON_RELEASE_MASK | GDK_SCROLL_MASK); taskbar_item_invalidate(self); return self; } sfwbar-1.0~beta13/src/taskbaritem.h000066400000000000000000000021531450657570000173140ustar00rootroot00000000000000#ifndef __TASKBARITEM_H__ #define __TASKBARITEM_H__ #include "sfwbar.h" #include "flowitem.h" #include "action.h" #define TASKBAR_ITEM_TYPE (taskbar_item_get_type()) #define TASKBAR_ITEM(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), TASKBAR_ITEM_TYPE, TaskbarItem)) #define TASKBAR_ITEM_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), TASKBAR_ITEM_TYPE, TaskbarItemClass)) #define IS_TASKBAR_ITEM(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), TASKBAR_ITEM_TYPE)) #define IS_TASKBAR_ITEMCLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), TASKBAR_ITEM_TYPE)) typedef struct _TaskbarItem TaskbarItem; typedef struct _TaskbarItemClass TaskbarItemClass; struct _TaskbarItem { FlowItem item; }; struct _TaskbarItemClass { FlowItemClass parent_class; }; typedef struct _TaskbarItemPrivate TaskbarItemPrivate; struct _TaskbarItemPrivate { GtkWidget *icon; GtkWidget *label; GtkWidget *taskbar; window_t *win; action_t **actions; gboolean invalid; GdkModifierType saved_modifiers; }; GType taskbar_item_get_type ( void ); GtkWidget *taskbar_item_new( window_t *win, GtkWidget *taskbar ); #endif sfwbar-1.0~beta13/src/tray.c000066400000000000000000000042531450657570000157630ustar00rootroot00000000000000/* This entire file is licensed under GNU General Public License v3.0 * * Copyright 2022- sfwbar maintainers */ #include "sfwbar.h" #include "trayitem.h" #include "tray.h" G_DEFINE_TYPE_WITH_CODE (Tray, tray, BASE_WIDGET_TYPE, G_ADD_PRIVATE (Tray)); static GList *trays; static GtkWidget *tray_get_child ( GtkWidget *self ) { TrayPrivate *priv; g_return_val_if_fail(IS_TRAY(self),NULL); priv = tray_get_instance_private(TRAY(self)); return priv->tray; } static GtkWidget *tray_mirror ( GtkWidget *src ) { GtkWidget *self; g_return_val_if_fail(IS_TRAY(src),NULL); self = tray_new(); flow_grid_copy_properties(self,src); base_widget_copy_properties(self,src); return self; } static void tray_destroy ( GtkWidget *self ) { trays = g_list_remove(trays,self); GTK_WIDGET_CLASS(tray_parent_class)->destroy(self); } static void tray_class_init ( TrayClass *kclass ) { BASE_WIDGET_CLASS(kclass)->get_child = tray_get_child; BASE_WIDGET_CLASS(kclass)->mirror = tray_mirror; GTK_WIDGET_CLASS(kclass)->destroy = tray_destroy; BASE_WIDGET_CLASS(kclass)->action_exec = NULL; } static void tray_init ( Tray *self ) { } GtkWidget *tray_new ( void ) { GtkWidget *self; TrayPrivate *priv; self = GTK_WIDGET(g_object_new(tray_get_type(), NULL)); priv = tray_get_instance_private(TRAY(self)); priv->tray = flow_grid_new(TRUE); gtk_grid_set_column_homogeneous(GTK_GRID(priv->tray),FALSE); gtk_container_add(GTK_CONTAINER(self),priv->tray); if(!trays) sni_init(); trays = g_list_append(trays,self); return self; } void tray_invalidate_all ( SniItem *sni ) { GList *iter; for(iter=trays; iter; iter=g_list_next(iter)) flow_item_invalidate(flow_grid_find_child(iter->data,sni)); } void tray_item_init_for_all ( SniItem *sni ) { GList *iter; for(iter=trays; iter; iter=g_list_next(iter)) if(iter->data) tray_item_new(sni,iter->data); } void tray_item_destroy ( SniItem *sni ) { GList *iter; for(iter=trays; iter; iter=g_list_next(iter)) flow_grid_delete_child(iter->data,sni); } void tray_update ( void ) { GList *iter; for(iter=trays; iter; iter=g_list_next(iter)) if(iter->data) flow_grid_update(iter->data); } sfwbar-1.0~beta13/src/tray.h000066400000000000000000000016461450657570000157730ustar00rootroot00000000000000#ifndef __TRAY_H__ #define __TRAY_H__ #include "basewidget.h" #include "flowgrid.h" #include "sni.h" #define TRAY_TYPE (tray_get_type()) #define TRAY(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), TRAY_TYPE, Tray)) #define TRAY_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), TRAY_TYPE, TrayClass)) #define IS_TRAY(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), TRAY_TYPE)) #define IS_TRAYCLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), TRAY_TYPE)) typedef struct _Tray Tray; typedef struct _TrayClass TrayClass; struct _Tray { BaseWidget item; }; struct _TrayClass { BaseWidgetClass parent_class; }; typedef struct _TrayPrivate TrayPrivate; struct _TrayPrivate { GtkWidget *tray; }; GType tray_get_type ( void ); GtkWidget *tray_new(); void tray_item_init_for_all ( SniItem *sni ); void tray_item_destroy ( SniItem *sni ); void tray_update ( void ); void tray_invalidate_all ( SniItem *sni ); #endif sfwbar-1.0~beta13/src/trayitem.c000066400000000000000000000154761450657570000166530ustar00rootroot00000000000000/* This entire file is licensed under GNU General Public License v3.0 * * Copyright 2022- sfwbar maintainers */ #include "sfwbar.h" #include "bar.h" #include "trayitem.h" #include "flowgrid.h" #include "scaleimage.h" G_DEFINE_TYPE_WITH_CODE (TrayItem, tray_item, FLOW_ITEM_TYPE, G_ADD_PRIVATE (TrayItem)); void tray_item_update ( GtkWidget *self ) { TrayItemPrivate *priv; gint icon=-1, pix=-1; g_return_if_fail(IS_TRAY_ITEM(self)); priv = tray_item_get_instance_private(TRAY_ITEM(self)); if(!priv->invalid) return; priv->invalid = FALSE; if(priv->sni->string[SNI_PROP_STATUS]) { switch(priv->sni->string[SNI_PROP_STATUS][0]) { case 'A': gtk_widget_set_name(priv->button,"tray_active"); icon = SNI_PROP_ICON; pix = SNI_PROP_ICONPIX; break; case 'N': gtk_widget_set_name(priv->button,"tray_attention"); icon = SNI_PROP_ATTN; pix = SNI_PROP_ATTNPIX; break; case 'P': gtk_widget_set_name(priv->button,"tray_passive"); icon = SNI_PROP_ICON; pix = SNI_PROP_ICONPIX; } } if(icon==-1) scale_image_set_image(priv->icon, NULL, NULL); else if(priv->sni->string[icon] && *(priv->sni->string[icon])) scale_image_set_image(priv->icon, priv->sni->string[icon], priv->sni->string[SNI_PROP_THEME]); else if(priv->sni->pixbuf[pix-SNI_PROP_ICONPIX]) scale_image_set_pixbuf(priv->icon, priv->sni->pixbuf[pix-SNI_PROP_ICONPIX]); if(priv->sni->string[SNI_PROP_LABEL] && *(priv->sni->string[SNI_PROP_LABEL])) { gtk_label_set_markup(GTK_LABEL(priv->label), priv->sni->string[SNI_PROP_LABEL]); if( priv->sni->string[SNI_PROP_LGUIDE] && *(priv->sni->string[SNI_PROP_LGUIDE]) ) gtk_label_set_width_chars(GTK_LABEL(priv->label), strlen(priv->sni->string[SNI_PROP_LGUIDE])); css_remove_class(priv->label, "hidden"); } else css_add_class(priv->label, "hidden"); } SniItem *tray_item_get_sni ( GtkWidget *self ) { TrayItemPrivate *priv; g_return_val_if_fail(IS_TRAY_ITEM(self),NULL); priv = tray_item_get_instance_private(TRAY_ITEM(self)); return priv->sni; } static gboolean tray_item_action_exec ( GtkWidget *self, gint slot, GdkEvent *ev ) { TrayItemPrivate *priv; GtkAllocation alloc,walloc; GdkRectangle geo; gchar *method = NULL, *dir; gint32 x,y, delta; g_return_val_if_fail(IS_TRAY_ITEM(self),FALSE); priv = tray_item_get_instance_private(TRAY_ITEM(self)); if(!ev || !priv->sni) return FALSE; if(ev->type == GDK_SCROLL) { if( ev->scroll.direction == GDK_SCROLL_DOWN || ev->scroll.direction == GDK_SCROLL_RIGHT ) delta=1; else delta=-1; if( ev->scroll.direction == GDK_SCROLL_DOWN || ev->scroll.direction == GDK_SCROLL_UP ) dir = "vertical"; else dir = "horizontal"; g_debug("sni %s: dimension: %s, delta: %d", priv->sni->dest, dir, delta); g_dbus_connection_call(sni_get_connection(), priv->sni->dest, priv->sni->path, priv->sni->host->item_iface, "Scroll", g_variant_new("(is)", delta, dir), NULL, G_DBUS_CALL_FLAGS_NONE, -1, NULL, NULL, NULL); return TRUE; } if(ev->type == GDK_BUTTON_RELEASE) { g_debug("sni %s: button: %d",priv->sni->dest, ev->button.button); if((ev->button.button == 1 && priv->sni->menu) || ev->button.button == 3) { if(priv->sni->menu_path) sni_get_menu(self, ev); else method = "ContextMenu"; } else if(ev->button.button == 1) method = "Activate"; else if(ev->button.button == 2) method = "SecondaryActivate"; else return FALSE; gdk_monitor_get_geometry(widget_get_monitor(self),&geo); gtk_widget_get_allocation(self,&alloc); gtk_widget_get_allocation(gtk_widget_get_toplevel(self),&walloc); switch(bar_get_toplevel_dir(self)) { case GTK_POS_RIGHT: x = geo.width - walloc.width + ev->button.x + alloc.x; y = ev->button.y + alloc.y; break; case GTK_POS_LEFT: x = walloc.width; y = ev->button.y + alloc.y; break; case GTK_POS_TOP: y = walloc.height; x = ev->button.x + alloc.x; break; default: y = geo.height - walloc.height; x = ev->button.x + alloc.x; } // call event at 0,0 to avoid menu popping up under the bar if(method) { g_debug("sni: calling %s on %s at ( %d, %d )", method, priv->sni->dest, x,y); g_dbus_connection_call(sni_get_connection(), priv->sni->dest, priv->sni->path, priv->sni->host->item_iface, method, g_variant_new("(ii)", 0, 0), NULL, G_DBUS_CALL_FLAGS_NONE, -1, NULL, NULL, NULL); } return TRUE; } return FALSE; } static gint tray_item_compare ( GtkWidget *a, GtkWidget *b, GtkWidget *parent ) { TrayItemPrivate *p1,*p2; g_return_val_if_fail(IS_TRAY_ITEM(a), 0); g_return_val_if_fail(IS_TRAY_ITEM(b), 0); p1 = tray_item_get_instance_private(TRAY_ITEM(a)); p2 = tray_item_get_instance_private(TRAY_ITEM(b)); return g_strcmp0(p1->sni->string[SNI_PROP_TITLE], p2->sni->string[SNI_PROP_TITLE]); } static void tray_item_invalidate ( GtkWidget *self ) { TrayItemPrivate *priv; if(!self) return; g_return_if_fail(IS_TRAY_ITEM(self)); priv = tray_item_get_instance_private(TRAY_ITEM(self)); flow_grid_invalidate(priv->tray); priv->invalid = TRUE; } static void tray_item_class_init ( TrayItemClass *kclass ) { BASE_WIDGET_CLASS(kclass)->action_exec = tray_item_action_exec; FLOW_ITEM_CLASS(kclass)->update = tray_item_update; FLOW_ITEM_CLASS(kclass)->compare = tray_item_compare; FLOW_ITEM_CLASS(kclass)->invalidate = tray_item_invalidate; FLOW_ITEM_CLASS(kclass)->get_parent = (void *(*)(GtkWidget *))tray_item_get_sni; } static void tray_item_init ( TrayItem *self ) { } GtkWidget *tray_item_new( SniItem *sni, GtkWidget *tray ) { GtkWidget *self; GtkWidget *box; TrayItemPrivate *priv; gint dir; g_return_val_if_fail(sni,NULL); self = GTK_WIDGET(g_object_new(tray_item_get_type(), NULL)); priv = tray_item_get_instance_private(TRAY_ITEM(self)); priv->button = gtk_button_new(); gtk_container_add(GTK_CONTAINER(self), priv->button); gtk_widget_set_name(priv->button, "tray_active"); gtk_widget_style_get(priv->button, "direction", &dir, NULL); box = gtk_grid_new(); gtk_container_add(GTK_CONTAINER(priv->button), box); flow_grid_child_dnd_enable(tray, self, priv->button); priv->icon = scale_image_new(); priv->label = gtk_label_new(""); priv->sni = sni; priv->tray = tray; priv->invalid = TRUE; gtk_grid_attach_next_to(GTK_GRID(box),priv->icon, NULL, dir, 1, 1); gtk_grid_attach_next_to(GTK_GRID(box), priv->label, priv->icon, dir, 1, 1); g_object_ref(self); flow_grid_add_child(tray, self); gtk_widget_add_events(GTK_WIDGET(self), GDK_SCROLL_MASK); return self; } sfwbar-1.0~beta13/src/trayitem.h000066400000000000000000000017601450657570000166470ustar00rootroot00000000000000#ifndef __SNIITEM_H__ #define __SNIITEM_H__ #include "sfwbar.h" #include "flowitem.h" #include "sni.h" #define TRAY_ITEM_TYPE (tray_item_get_type()) #define TRAY_ITEM(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), TRAY_ITEM_TYPE, TrayItem)) #define TRAY_ITEM_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), TRAY_ITEM_TYPE, TrayItemClass)) #define IS_TRAY_ITEM(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), TRAY_ITEM_TYPE)) #define IS_TRAY_ITEMCLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), TRAY_ITEM_TYPE)) typedef struct _TrayItem TrayItem; typedef struct _TrayItemClass TrayItemClass; struct _TrayItem { FlowItem item; }; struct _TrayItemClass { FlowItemClass parent_class; }; typedef struct _TrayItemPrivate TrayItemPrivate; struct _TrayItemPrivate { SniItem *sni; GtkWidget *button; GtkWidget *icon; GtkWidget *label; GtkWidget *tray; gboolean invalid; }; GType tray_item_get_type ( void ); GtkWidget *tray_item_new( SniItem *win, GtkWidget *tray ); #endif sfwbar-1.0~beta13/src/wayland.c000066400000000000000000000037001450657570000164370ustar00rootroot00000000000000/* This entire file is licensed under GNU General Public License v3.0 * * Copyright 2023- sfwbar maintainers */ #include "sfwbar.h" #include "wayland.h" #include "xdg-output-unstable-v1.h" #include "idle-inhibit-unstable-v1.h" #include "wlr-foreign-toplevel-management-unstable-v1.h" #include "wlr-layer-shell-unstable-v1.h" #include void foreign_toplevel_register (struct wl_registry *registry, uint32_t name); void xdg_output_register (struct wl_registry *registry, uint32_t name); void layer_shell_register (struct wl_registry *, uint32_t , uint32_t ); void shm_register (struct wl_registry *registry, uint32_t name ); void wayland_monitor_probe ( void ); static void handle_global(void *data, struct wl_registry *registry, uint32_t name, const gchar *interface, uint32_t version) { if (!g_strcmp0(interface,zxdg_output_manager_v1_interface.name)) xdg_output_register(registry,name); else if (!g_strcmp0(interface, wl_shm_interface.name)) shm_register(registry, name); else if (!g_strcmp0(interface, zwlr_layer_shell_v1_interface.name)) layer_shell_register(registry, name, version); } static void handle_global_remove(void *data, struct wl_registry *registry, uint32_t name) { } static const struct wl_registry_listener registry_listener = { .global = handle_global, .global_remove = handle_global_remove }; void wayland_init ( void ) { struct wl_display *wdisp; struct wl_registry *registry; wdisp = gdk_wayland_display_get_wl_display(gdk_display_get_default()); if(!wdisp) g_error("Can't get wayland display\n"); registry = wl_display_get_registry(wdisp); wl_registry_add_listener(registry, ®istry_listener, NULL); wl_display_roundtrip(wdisp); wayland_monitor_probe(); GdkMonitor *mon = wayland_monitor_get_default(); g_debug("default output: %s",(gchar *)g_object_get_data(G_OBJECT(mon),"xdg_name")); wl_display_roundtrip(wdisp); wl_display_roundtrip(wdisp); } sfwbar-1.0~beta13/src/wayland.h000066400000000000000000000007341450657570000164500ustar00rootroot00000000000000#ifndef __WAYLAND_H__ #define __WAYLAND_H__ #include void wayland_init ( void ); void wayland_ipc_init ( void ); void wayland_set_idle_inhibitor ( GtkWidget *widget, gboolean inhibit ); void xdg_output_new ( GdkMonitor *gmon ); void xdg_output_destroy ( GdkMonitor *gmon ); void foreign_toplevel_activate ( gpointer tl ); gboolean xdg_output_check ( void ); gboolean foreign_toplevel_is_active ( void ); GdkMonitor *wayland_monitor_get_default ( void ); #endif sfwbar-1.0~beta13/src/wayland/000077500000000000000000000000001450657570000162735ustar00rootroot00000000000000sfwbar-1.0~beta13/src/wayland/foreign-toplevel.c000066400000000000000000000150261450657570000217240ustar00rootroot00000000000000/* This entire file is licensed under GNU General Public License v3.0 * * Copyright 2022- sfwbar maintainers */ #include #include "../sfwbar.h" #include "../sway_ipc.h" #include "../wintree.h" #include "wlr-foreign-toplevel-management-unstable-v1.h" typedef struct zwlr_foreign_toplevel_handle_v1 wlr_fth; static struct zwlr_foreign_toplevel_manager_v1 *toplevel_manager; gboolean foreign_toplevel_is_active ( void ) { return (toplevel_manager!=NULL); } void foreign_toplevel_activate ( gpointer tl ) { zwlr_foreign_toplevel_handle_v1_unset_minimized(tl); zwlr_foreign_toplevel_handle_v1_activate(tl,gdk_wayland_seat_get_wl_seat( gdk_display_get_default_seat(gdk_display_get_default()))); } static void toplevel_handle_app_id(void *data, wlr_fth *tl, const gchar *appid) { wintree_set_app_id(tl,appid); } static void toplevel_handle_title(void *data, wlr_fth *tl, const gchar *title) { wintree_set_title(tl,title); } static void toplevel_handle_closed(void *data, wlr_fth *tl) { wintree_window_delete(tl); zwlr_foreign_toplevel_handle_v1_destroy(tl); } static void toplevel_handle_done(void *data, wlr_fth *tl) { wintree_log(tl); } static void toplevel_handle_state(void *data, wlr_fth *tl, struct wl_array *state) { uint32_t *entry; window_t *win; win = wintree_from_id(tl); if(!win) return; win->state = 0; wl_array_for_each(entry, state) switch(*entry) { case ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_STATE_MINIMIZED: win->state |= WS_MINIMIZED; break; case ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_STATE_MAXIMIZED: win->state |= WS_MAXIMIZED; break; case ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_STATE_FULLSCREEN: win->state |= WS_FULLSCREEN; break; case ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_STATE_ACTIVATED: win->state |= WS_FOCUSED; wintree_set_focus(win->uid); break; } if(wintree_is_focused(win->uid) && !(win->state & WS_FOCUSED)) wintree_set_focus(NULL); g_debug("foreign toplevel state for %p: %s%s%s%s", win->uid, win->state & WS_FOCUSED ? "Activated, " : "", win->state & WS_MINIMIZED ? "Minimized, " : "", win->state & WS_MAXIMIZED ? "Maximized, " : "", win->state & WS_FULLSCREEN ? "Fullscreen" : "" ); } static void toplevel_handle_parent(void *data, wlr_fth *tl, wlr_fth *pt) { } static gchar *toplevel_output_name_get ( struct wl_output *output ) { GdkDisplay *disp; gint i; disp = gdk_display_get_default(); for(i=0;ioutputs,name,(GCompareFunc)g_strcmp0); if(!link) return; g_free(link->data); win->outputs = g_list_delete_link(win->outputs,link); wintree_commit(win); } static void toplevel_handle_output_enter(void *data, wlr_fth *tl, struct wl_output *output) { char *name; window_t *win; name = toplevel_output_name_get(output); if(!name) return; win = wintree_from_id(tl); if(!win) return; if(g_list_find_custom(win->outputs,name,(GCompareFunc)g_strcmp0)) return; win->outputs = g_list_prepend(win->outputs,g_strdup(name)); wintree_commit(win); } static const struct zwlr_foreign_toplevel_handle_v1_listener toplevel_impl = { .title = toplevel_handle_title, .app_id = toplevel_handle_app_id, .output_enter = toplevel_handle_output_enter, .output_leave = toplevel_handle_output_leave, .state = toplevel_handle_state, .done = toplevel_handle_done, .closed = toplevel_handle_closed, .parent = toplevel_handle_parent }; static void toplevel_manager_handle_toplevel(void *data, struct zwlr_foreign_toplevel_manager_v1 *toplevel_manager, wlr_fth *tl) { window_t *win; win = wintree_window_init(); win->uid = tl; wintree_window_append(win); zwlr_foreign_toplevel_handle_v1_add_listener(tl, &toplevel_impl, NULL); } static void toplevel_manager_handle_finished(void *data, struct zwlr_foreign_toplevel_manager_v1 *toplevel_manager) { zwlr_foreign_toplevel_manager_v1_destroy(toplevel_manager); } static const struct zwlr_foreign_toplevel_manager_v1_listener toplevel_manager_impl = { .toplevel = toplevel_manager_handle_toplevel, .finished = toplevel_manager_handle_finished, }; static void foreign_toplevel_focus ( gpointer id ) { zwlr_foreign_toplevel_handle_v1_unset_minimized(id); foreign_toplevel_activate(id); } static struct wintree_api ft_wintree_api = { .minimize = (void (*)(void *)) zwlr_foreign_toplevel_handle_v1_set_minimized, .unminimize = (void (*)(void *)) zwlr_foreign_toplevel_handle_v1_unset_minimized, .maximize = (void (*)(void *)) zwlr_foreign_toplevel_handle_v1_set_maximized, .unmaximize = (void (*)(void *)) zwlr_foreign_toplevel_handle_v1_unset_maximized, .close = (void (*)(void *)) zwlr_foreign_toplevel_handle_v1_close, .focus = foreign_toplevel_focus }; static void foreign_toplevel_register (struct wl_registry *registry, uint32_t name) { if(ipc_get()) return; toplevel_manager = wl_registry_bind(registry, name, &zwlr_foreign_toplevel_manager_v1_interface,3); zwlr_foreign_toplevel_manager_v1_add_listener(toplevel_manager, &toplevel_manager_impl, NULL); wintree_api_register(&ft_wintree_api); } static void handle_global(void *data, struct wl_registry *registry, uint32_t name, const gchar *interface, uint32_t version) { if (!g_strcmp0(interface,zwlr_foreign_toplevel_manager_v1_interface.name)) foreign_toplevel_register(registry,name); } static void handle_global_remove(void *data, struct wl_registry *registry, uint32_t name) { } static const struct wl_registry_listener registry_listener = { .global = handle_global, .global_remove = handle_global_remove }; void wayland_ipc_init ( void ) { struct wl_display *wdisp; struct wl_registry *registry; wdisp = gdk_wayland_display_get_wl_display(gdk_display_get_default()); if(!wdisp) g_error("Can't get wayland display\n"); registry = wl_display_get_registry(wdisp); wl_registry_add_listener(registry, ®istry_listener, NULL); wl_display_roundtrip(wdisp); wl_display_roundtrip(wdisp); wl_display_roundtrip(wdisp); } sfwbar-1.0~beta13/src/wayland/probe.c000066400000000000000000000075111450657570000175520ustar00rootroot00000000000000/* This entire file is licensed under GNU General Public License v3.0 * * Copyright 2023- sfwbar maintainers */ #include #include #include #include #include "wlr-layer-shell-unstable-v1.h" static GdkMonitor *default_monitor; static struct wl_shm *shm; static struct zwlr_layer_shell_v1 *layer_shell; void shm_register (struct wl_registry *registry, uint32_t name ) { shm = wl_registry_bind(registry, name, &wl_shm_interface, 1); } void layer_shell_register (struct wl_registry *registry, uint32_t name, uint32_t version) { layer_shell = wl_registry_bind(registry, name, &zwlr_layer_shell_v1_interface,version); } GdkMonitor *wayland_monitor_get_default ( void ) { GdkDisplay *gdisp = gdk_display_get_default(); gint n, i; n = gdk_display_get_n_monitors(gdisp); for(i=0; i= 0) shm_unlink(name); g_free(name); } while (--retries > 0 && errno == EEXIST && fd < 0 ); if (fd < 0) return; if (ftruncate(fd, size) < 0) { close(fd); return; } shm_data = mmap(NULL, 4, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); if (shm_data == MAP_FAILED) { close(fd); return; } pool = wl_shm_create_pool(shm, fd, size); buffer = wl_shm_pool_create_buffer(pool, 0, 1, 1, 4, WL_SHM_FORMAT_ARGB8888); wl_shm_pool_destroy(pool); memset(shm_data, 0, size); surface = wl_compositor_create_surface(compositor); wl_surface_add_listener(surface,&surface_listener,NULL); layer_surface = zwlr_layer_shell_v1_get_layer_surface(layer_shell, surface, NULL, ZWLR_LAYER_SHELL_V1_LAYER_TOP, "sfwbar-test"); zwlr_layer_surface_v1_set_anchor(layer_surface, ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT); zwlr_layer_surface_v1_set_size(layer_surface,1,1); zwlr_layer_surface_v1_add_listener(layer_surface, &layer_surface_listener,NULL); wl_surface_commit(surface); wl_display_roundtrip(display); wl_surface_attach(surface, buffer, 0, 0); wl_surface_commit(surface); wl_display_roundtrip(display); zwlr_layer_surface_v1_destroy(layer_surface); wl_surface_destroy(surface); wl_buffer_destroy(buffer); munmap(shm_data,size); close(fd); zwlr_layer_shell_v1_destroy(layer_shell); wl_shm_destroy(shm); } sfwbar-1.0~beta13/src/wayland/xdg-output.c000066400000000000000000000045461450657570000205700ustar00rootroot00000000000000/* This entire file is licensed under GNU General Public License v3.0 * * Copyright 2022- sfwbar maintainers */ #include #include #include "xdg-output-unstable-v1.h" static struct zxdg_output_manager_v1 *xdg_output_manager; static void xdg_output_noop () { } static void xdg_output_handle_name ( void *monitor, struct zxdg_output_v1 *xdg_output, const gchar *name ) { g_object_set_data_full(G_OBJECT(monitor),"xdg_name",g_strdup(name),g_free); } static void xdg_output_handle_done ( void *monitor, struct zxdg_output_v1 *xdg_output ) { zxdg_output_v1_destroy(xdg_output); } static const struct zxdg_output_v1_listener xdg_output_listener = { .logical_position = xdg_output_noop, .logical_size = xdg_output_noop, .done = xdg_output_handle_done, .name = xdg_output_handle_name, .description = xdg_output_noop, }; void xdg_output_new ( GdkMonitor *monitor ) { struct wl_output *output; struct zxdg_output_v1 *xdg; if(!monitor || !xdg_output_manager) return; output = gdk_wayland_monitor_get_wl_output(monitor); if(!output) return; xdg = zxdg_output_manager_v1_get_xdg_output(xdg_output_manager, output); if(xdg) { zxdg_output_v1_add_listener(xdg,&xdg_output_listener,monitor); g_object_set_data(G_OBJECT(monitor),"xdg_output",xdg); } } void xdg_output_destroy ( GdkMonitor *gmon ) { struct zxdg_output_v1 *xdg; if(!gmon || !xdg_output_manager) return; xdg = g_object_get_data(G_OBJECT(gmon),"xdg_output"); if(xdg) zxdg_output_v1_destroy(xdg); } gboolean xdg_output_check ( void ) { GdkDisplay *gdisp; gint i; if(!xdg_output_manager) return TRUE; gdisp = gdk_display_get_default(); for(i=0;i void window_ref ( GtkWidget *self, GtkWidget *ref ); void window_unref ( GtkWidget *ref, GtkWidget *self ); gboolean window_ref_check ( GtkWidget *self ); #endif sfwbar-1.0~beta13/src/wintree.c000066400000000000000000000217671450657570000164720ustar00rootroot00000000000000/* This entire file is licensed under GNU General Public License v3.0 * * Copyright 2020- sfwbar maintainers */ #include #include "sfwbar.h" #include "wintree.h" #include "taskbar.h" #include "switcher.h" static struct wintree_api api; static GList *wt_list; static GList *appid_map; static GList *appid_filter_list; static GList *title_filter_list; static gpointer wt_focus; static gboolean disown; struct appid_mapper{ GRegex *regex; gchar *app_id; }; #define api_call(x) if(api.x) api.x(id); void wintree_minimize ( gpointer id ) { api_call(minimize) } void wintree_unminimize ( gpointer id ) { api_call(unminimize) } void wintree_maximize ( gpointer id ) { api_call(maximize) } void wintree_unmaximize ( gpointer id ) { api_call(unmaximize) } void wintree_focus ( gpointer id ) { api_call(focus) } void wintree_close ( gpointer id ) { api_call(close) } void wintree_free_workspace ( gpointer id ) { api_call(free_workspace) } gint wintree_comp_workspace ( gpointer id1, gpointer id2 ) { if(api.comp_workspace) return api.comp_workspace(id1, id2); else return id1 - id2; } void wintree_api_register ( struct wintree_api *new ) { api = *new; } void wintree_set_disown ( gboolean new ) { disown = new; } gboolean wintree_get_disown ( void ) { return disown; } gchar *wintree_get_active ( void ) { window_t *win; win = wintree_from_id(wintree_get_focus()); return win?win->title:""; } window_t *wintree_window_init ( void ) { window_t *w; w = g_malloc0(sizeof(window_t)); w->pid=-1; return w; } void wintree_log ( gpointer id ) { window_t *win; win = wintree_from_id(id); if(win) g_debug("app_id: '%s', title '%s'", win->appid?win->appid:"(null)",win->title?win->title:"(null)"); } gint wintree_compare ( window_t *a, window_t *b) { gint s; s = g_strcmp0(a->title,b->title); if(s) return s; return GPOINTER_TO_INT(a->uid)-GPOINTER_TO_INT(b->uid); } void wintree_set_focus ( gpointer id ) { GList *iter; if(wt_focus == id) return; wintree_commit(wintree_from_id(wt_focus)); wt_focus = id; for(iter=wt_list; iter; iter=g_list_next(iter) ) if (((window_t *)(iter->data))->uid == id) break; if(!iter) return; wt_list = g_list_remove_link(wt_list, iter); if(wt_list) wt_list->prev = iter; iter->next = wt_list; wt_list = iter; wintree_commit(wt_list->data); g_idle_add((GSourceFunc)base_widget_emit_trigger, "window_focus"); } gpointer wintree_get_focus ( void ) { return wt_focus; } gboolean wintree_is_focused ( gpointer id ) { return ( id == wt_focus ); } window_t *wintree_from_id ( gpointer id ) { GList *item; for(item = wt_list; item; item = g_list_next(item) ) if ( ((window_t *)(item->data))->uid == id ) break; if(!item) return NULL; return item->data; } window_t *wintree_from_pid ( gint64 pid ) { GList *item; for(item = wt_list; item; item = g_list_next(item) ) if ( ((window_t *)(item->data))->pid == pid ) break; if(!item) return NULL; return item->data; } void wintree_commit ( window_t *win ) { if(!win) return; taskbar_invalidate_all(win,FALSE); switcher_invalidate(win); } void wintree_set_title ( gpointer wid, const gchar *title ) { window_t *win; if(!title) return; win = wintree_from_id(wid); if(!win) return; if(!g_strcmp0(win->title,title)) return; g_free(win->title); win->title = g_strdup(title); wintree_commit(win); } void wintree_set_app_id ( gpointer wid, const gchar *app_id) { window_t *win; if(!app_id) return; win = wintree_from_id(wid); if(!win) return; taskbar_reparent_item(win,app_id); g_free(win->appid); win->appid = g_strdup(app_id); if(!win->title) win->title = g_strdup(app_id); wintree_commit(win); } void wintree_set_float ( gpointer wid, gboolean floating ) { window_t *win; win = wintree_from_id(wid); if(!win) return; win->floating = floating; wintree_commit(win); } void wintree_window_append ( window_t *win ) { if(!win) return; if( !win->title ) win->title = g_strdup(""); if(! win->appid) win->appid = g_strdup(""); if( !win->valid ) { taskbar_init_item (win); win->valid = TRUE; } if( !win->switcher && (win->title || win->appid) ) switcher_window_init(win); if(g_list_find(wt_list,win)==NULL) wt_list = g_list_append (wt_list,win); wintree_commit(win); } void wintree_window_delete ( gpointer id ) { GList *item; window_t *win; for(item = wt_list; item; item = g_list_next(item) ) if ( ((window_t *)(item->data))->uid == id ) break; if(!item) return; win = item->data; if(!win) return; taskbar_destroy_item (win); switcher_window_delete(win); g_free(win->appid); g_free(win->title); g_list_free_full(win->outputs,g_free); wintree_free_workspace(win->workspace); wt_list = g_list_delete_link(wt_list,item); g_free(win); } GList *wintree_get_list ( void ) { return wt_list; } void wintree_appid_map_add ( gchar *pattern, gchar *appid ) { struct appid_mapper *map; GList *iter; for(iter=appid_map;iter;iter=g_list_next(iter)) if(!g_strcmp0(pattern, g_regex_get_pattern(((struct appid_mapper *)iter->data)->regex))) return; map = g_malloc0(sizeof(struct appid_mapper)); map->regex = g_regex_new(pattern,0,0,NULL); if(!map->regex) { g_message("MapAppId: invalid paatern '%s'",pattern); g_free(map); return; } map->app_id = g_strdup(appid); appid_map = g_list_prepend(appid_map,map); } gchar *wintree_appid_map_lookup ( gchar *title ) { GList *iter; for(iter=appid_map;iter;iter=g_list_next(iter)) if(g_regex_match (((struct appid_mapper *)iter->data)->regex, title, 0, NULL)) return ((struct appid_mapper *)iter->data)->app_id; return NULL; } void wintree_filter_appid ( gchar *pattern ) { GList *iter; GRegex *regex; for(iter=appid_filter_list;iter;iter=g_list_next(iter)) if(!g_strcmp0(pattern, g_regex_get_pattern(((struct appid_mapper *)iter->data)->regex))) return; regex = g_regex_new(pattern,0,0,NULL); if(!regex) return; appid_filter_list = g_list_prepend(appid_filter_list, regex); } void wintree_filter_title ( gchar *pattern ) { GList *iter; GRegex *regex; for(iter=title_filter_list;iter;iter=g_list_next(iter)) if(!g_strcmp0(pattern, g_regex_get_pattern(((struct appid_mapper *)iter->data)->regex))) return; regex = g_regex_new(pattern,0,0,NULL); if(!regex) return; title_filter_list = g_list_prepend(title_filter_list, regex); } gboolean wintree_is_filtered ( window_t *win ) { return (regex_match_list(appid_filter_list, win->appid) || regex_match_list(title_filter_list, win->title)); } static gint x_step, y_step, x_origin, y_origin; static gboolean check_pid; static gboolean placer; void wintree_placer_conf( gint xs, gint ys, gint xo, gint yo, gboolean pid ) { x_step = MAX(1,xs); y_step = MAX(1,ys); x_origin = xo; y_origin = yo; check_pid = !pid; placer = TRUE; } gboolean wintree_placer_state ( void ) { return placer; } gboolean wintree_placer_check ( gint pid ) { GList *iter; gint count = 0; if(!placer) return FALSE; for(iter = wt_list; iter; iter = g_list_next(iter) ) if ( ((window_t *)(iter->data))->pid == pid ) count++; return (count<2); } static int comp_int ( const void *x1, const void *x2) { return ( *(int*)x1 - *(int*)x2 ); } void wintree_placer_calc ( gint nobs, GdkRectangle *obs, GdkRectangle output, GdkRectangle *win ) { gint xoff,yoff; gint *x,*y; gint i,j,c; gboolean success; xoff = (win->x*2+win->width)/2 - (output.x*2+output.width)/2; yoff = (win->y*2+win->height)/2 - (output.y*2+output.height)/2; if((xoff<-1)||(xoff>1)||(yoff<-1)||(yoff>1)) return; x = g_malloc((nobs+1)*sizeof(int)); y = g_malloc((nobs+1)*sizeof(int)); for(c=0;cx = output.x+x_origin*output.width/100; win->y = output.y+y_origin*output.height/100; do { success=TRUE; for(c=0;cx==obs[c].x) && (win->y==obs[c].y)) success=FALSE; if (success) break; win->x += output.width*x_step/100; win->y += output.height*y_step/100;; } while((win->x+win->width)<(output.x+output.width) && (win->y+win->height)<(output.y+output.height)); for(j=nobs;j>=0;j--) for(i=nobs;i>=0;i--) { success=TRUE; for(c=0;cwidth-1)(obs[c].x+obs[c].width-1)) || ((y[j]+win->height-1)(obs[c].y+obs[c].height-1)))) success=FALSE; if((x[i]width>output.x+output.width)|| (y[j]height>output.y+output.height)) success=FALSE; if(success) { win->x = x[i]; win->y = y[j]; } } g_free(x); g_free(y); } sfwbar-1.0~beta13/src/wintree.h000066400000000000000000000046261450657570000164720ustar00rootroot00000000000000#ifndef __WINTREE_H__ #define __WINTREE_H__ #include typedef struct wt_window { GtkWidget *switcher; gchar *title; gchar *appid; GList *outputs; gchar *workspace; gint64 pid; gpointer uid; guint16 state; gboolean floating; gboolean valid; } window_t; struct wintree_api { void (*minimize) ( void *); void (*unminimize) ( void *); void (*maximize) ( void *); void (*unmaximize) ( void *); void (*focus) ( void *); void (*close) ( void *); void (*free_workspace) ( void *); gint (*comp_workspace) ( void *, void *); }; void wintree_api_register ( struct wintree_api *new ); window_t *wintree_window_init ( void ); window_t *wintree_from_id ( gpointer id ); window_t *wintree_from_pid ( gint64 pid ); void wintree_window_append ( window_t *win ); void wintree_window_delete ( gpointer id ); void wintree_commit ( window_t *win ); void wintree_log ( gpointer id ); void wintree_set_focus ( gpointer id ); void wintree_set_active ( gchar *title ); void wintree_set_title ( gpointer wid, const gchar *title ); void wintree_set_app_id ( gpointer wid, const gchar *app_id); void wintree_set_float ( gpointer wid, gboolean floating ); void wintree_focus ( gpointer id ); void wintree_close ( gpointer id ); void wintree_minimize ( gpointer id ); void wintree_maximize ( gpointer id ); void wintree_unminimize ( gpointer id ); void wintree_unmaximize ( gpointer id ); gint wintree_comp_workspace ( gpointer id1, gpointer id2 ); gpointer wintree_get_focus ( void ); gchar *wintree_get_active ( void ); gboolean wintree_is_focused ( gpointer id ); GList *wintree_get_list ( void ); gint wintree_compare ( window_t *a, window_t *b); void wintree_appid_map_add ( gchar *pattern, gchar *appid ); gchar *wintree_appid_map_lookup ( gchar *title ); void wintree_filter_appid ( gchar *pattern ); void wintree_filter_title ( gchar *pattern ); gboolean wintree_is_filtered ( window_t *win ); void wintree_placer_conf( gint xs, gint ys, gint xo, gint yo, gboolean pid ); gboolean wintree_placer_state ( void ); gboolean wintree_placer_check ( gint pid ); void wintree_placer_calc ( gint nobs, GdkRectangle *obs, GdkRectangle output, GdkRectangle *win ); gboolean wintree_get_disown ( void ); void wintree_set_disown ( gboolean new ); enum { WS_FOCUSED = 1<<0, WS_MINIMIZED = 1<<1, WS_MAXIMIZED = 1<<2, WS_FULLSCREEN = 1<<3, WS_USERSTATE = 1<<5, WS_USERSTATE2 = 1<<6, WS_CHILDREN = 1<<7 }; #endif