From fc1c079170ce7a35bd5c097f6d9fde70ba575be4 Mon Sep 17 00:00:00 2001 From: Adrian Iain Lam Date: Thu, 25 Aug 2016 04:15:26 +0800 Subject: [PATCH] Initial commit --- README.md | 86 +++++++++++++++++++++++++++++++++++++++++ icon.svg | 88 ++++++++++++++++++++++++++++++++++++++++++ indicator-keyboard-led.py | 96 ++++++++++++++++++++++++++++++++++++++++++++++ screenshots/sc1.png | Bin 0 -> 4154 bytes screenshots/sc2.png | Bin 0 -> 6940 bytes screenshots/sc3.png | Bin 0 -> 2312 bytes 6 files changed, 270 insertions(+) create mode 100644 README.md create mode 100644 icon.svg create mode 100755 indicator-keyboard-led.py create mode 100644 screenshots/sc1.png create mode 100644 screenshots/sc2.png create mode 100644 screenshots/sc3.png diff --git a/README.md b/README.md new file mode 100644 index 0000000..86240b9 --- /dev/null +++ b/README.md @@ -0,0 +1,86 @@ +# indicator-keyboard-led - simulate keyboard lock keys LED + +This is a Unity application indicator designed for keyboards without lock +keys LED. It allows the user to check the state of the three locks (Caps lock, +Num lock and Scroll lock) without requiring any mouse or keyboard action. It +also allows the lock keys to be toggled with mouse clicks, which could be +useful for keyboards without Scroll lock keys or malfunctioning keyboards. + +## Screenshots + +![indicator default][sc1] +Default appearance of the indicator with Num lock on and Caps and Scroll locks +off. + +![indicator menu][sc2] +Menu of the indicator, shown on click. The locks can be toggled by clicking +the respective item in the menu. + +![indicator short][sc3] +Alternative (short) appearance of the indicator. + +[sc1]: screenshots/sc1.png +[sc2]: screenshots/sc2.png +[sc3]: screenshots/sc3.png + +## Dependencies + - Python 3 (*) + - GTK+ 3 (*) + - AppIndicator 3 (*) + - Python 3 GObject introspection (python3-gi) + - xdotool + +Those marked with (*) are probably installed by default in recent Ubuntu +distributions. To install the rest, run: + + sudo apt-get install python3-gi xdotool + +## Usage + + 1. Install the dependencies listed above. + 2. Clone this repository. + 3. Add the script as a startup application. (Use option `--short` for short + appearance if desired.) + 4. Run the script manually for the first time. (Alternatively, log out + and log in again.) + 5. The indicator should be shown at the top right corner, with a filled circle + representing a lock turned on and an unfilled circle representing a lock + turned off. + 6. Clicking on the indicator should result in a menu with the three locks. + Clicking on the menu item would cause the corresponding lock to toggle. + +## License + +The program "indicator-keyboard-led.py" is released under the MIT License. +Please refer to the file for the full text of the license. + +The icon "icon.svg" is released to the public domain. + +## Credits + +I would like to thank [Tobias Schlitt](https://github.com/tobyS), who wrote +[indicator-chars](https://github.com/tobyS/indicator-chars) which I used +as a reference when writing this software. + +The icon used in the indicator (icon.svg) is modified from the file +"emblem-readonly.svg" by [Jakub Steiner](http://jimmac.musichall.cz) +who released it to the public domain for the +[Tango Icon Library](http://tango.freedesktop.org/Tango_Icon_Library). + +--- + +## Motivation + +I was a user of [indicator-keylock][ind-kl], but only one key lock can be shown +on the panel. I didn't like the Notify OSD events either. + +I then came across [lks-indicator][lks] and [indicator-xbdmod][xbdmod], but +I didn't like the fact that they refresh the indicator on a regular time +interval (every *x* milliseconds) rather than on state changes (only when +the locks are toggled). + +I also thought it would be fun to be able to toggle the locks on-screen. + +[ind-kl]: https://launchpad.net/~tsbarnes/+archive/ubuntu/indicator-keylock +[lks]: https://github.com/SergKolo/lks-indicator +[xbdmod]: https://github.com/sneetsher/indicator-xkbmod diff --git a/icon.svg b/icon.svg new file mode 100644 index 0000000..d711e58 --- /dev/null +++ b/icon.svg @@ -0,0 +1,88 @@ + + + + + + + image/svg+xml + + + + Jakub Steiner + + + Adrian I Lam + http://jimmac.musichall.cz + + Caps Lock icon + caps lock + + + + + + + + + + + + diff --git a/indicator-keyboard-led.py b/indicator-keyboard-led.py new file mode 100755 index 0000000..44e02a0 --- /dev/null +++ b/indicator-keyboard-led.py @@ -0,0 +1,96 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +# +# indicator-keyboard-led - simulate keyboard lock keys LED +# Copyright (c) 2016 Adrian I Lam +# +# 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 AUTHOR 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. +# +# I would like to thank Tobias Schlitt , who wrote +# indicator-chars which I used +# as a reference when writing this software. + +import signal +import subprocess +from os import path +import argparse +import gi +gi.require_version('Gdk', '3.0') +gi.require_version('Gtk', '3.0') +gi.require_version('AppIndicator3', '0.1') +from gi.repository import Gdk, Gtk, AppIndicator3 + +class IndicatorKeyboardLED: + locks = { 'caps': 'Caps', 'num': 'Num', 'scr': 'Scroll' } + + def __init__(self, short=False): + self.indicator = AppIndicator3.Indicator.new( + 'indicator-keyboard-led', + path.join(path.dirname(path.realpath(__file__)), 'icon.svg'), + AppIndicator3.IndicatorCategory.APPLICATION_STATUS) + self.indicator.set_status(AppIndicator3.IndicatorStatus.ACTIVE) + + if short: + self.locks = { 'caps': 'C', 'num': 'N', 'scr': 'S' } + + keymap = Gdk.Keymap.get_default() + keymap.connect('state-changed', self.update_indicator) + self.update_indicator(keymap) + + menu = Gtk.Menu() + items = { + 'caps' : Gtk.MenuItem.new_with_label('Caps Lock'), + 'num' : Gtk.MenuItem.new_with_label('Num Lock'), + 'scr' : Gtk.MenuItem.new_with_label('Scroll Lock') + } + menu.append(items['caps']) + menu.append(items['num']) + menu.append(items['scr']) + + items['caps'].connect('activate', self.send_keypress, 'Caps_Lock') + items['num' ].connect('activate', self.send_keypress, 'Num_Lock') + items['scr' ].connect('activate', self.send_keypress, 'Scroll_Lock') + + self.indicator.set_menu(menu) + menu.show_all() + + def update_indicator(self, keymap): + label = '⚫' if keymap.get_caps_lock_state() else '⚪' + label += self.locks['caps'] + ' ' + label += '⚫' if keymap.get_num_lock_state() else '⚪' + label += self.locks['num'] + ' ' + label += '⚫' if keymap.get_scroll_lock_state() else '⚪' + label += self.locks['scr'] + self.indicator.set_label(label, '') + + def send_keypress(self, menuitem, keystroke): + subprocess.call(['xdotool', 'key', keystroke]) + +if __name__ == '__main__': + signal.signal(signal.SIGINT, lambda signum, frame: Gtk.main_quit()) + + parser = argparse.ArgumentParser( + description='indicator-keyboard-led - simulate keyboard lock keys LED') + parser.add_argument('-s', '--short', dest='short', action='store_true', + help='use short label, i.e. ⚫C ⚫N ⚫S instead of ⚫Caps ⚫Num ⚫Scroll', + required=False) + args = parser.parse_args() + + IndicatorKeyboardLED(short=args.short) + Gtk.main() diff --git a/screenshots/sc1.png b/screenshots/sc1.png new file mode 100644 index 0000000000000000000000000000000000000000..394422e20c28d876d7e37578fa9eac9bbb074c2e GIT binary patch literal 4154 zcmV-A5XJ9_P)w&J&dmfR&2lH=l%=Fgq$bjCzARaRClUiwZj z;1vY!XJ*)n-w@F7q1nx~v8*t6_DqIh0HC70;!0H|07{69OHN4!0A1H-zq}Be*T`k9 z_$>ny#^d&|jN!1y7!hUMjD1VdWze998HQ05<)c-rT`pH$eMUycn5RYwf-rbc_Qp*c zf9ff;;uiuYjN40YePm=0-?sJpuLN*xxV&M11#fpW=`qc^3&x~N=S%?Dv-hi_qN2#A z@4x?k_nv(ZKQypwm(J@qtVamlAa8>(exE<&4`?O^Kn%;o+8DddZr*F$|*xP6TL=PnH8d-2@BI zq&hfa5h4vV%zNBg=K+ro?w@r_f`GvAT`D@dd-K*KC5-g5cuWhF2%&2#mD^{%J27VO ztAAMwqM$GwCY-y<9FY6|kkU7wTUHeNGk8qPD*05-klKxU>4iR-2?78PRumrF`SIHQ zjMa7{9z%c-Ky^Rr>j{t_>n;e@)@z#9*d`>)b%rgZGfdc;YB|0Cuu%_o%dq3Bq9dC> z-gF`4BfKqaU=Ysdu?+h2oS|(41$kRn6qE&!Eu~#%2M1_6@%VAjQ)LhUMA<|(F-9^d zngCPd@hKtMFfjno44dd+I6)!^5rVuyL-x5flK`TeHYUaq9aN(!6&~5VdeenqEu~tS z2tY!=8PjWCTCkIkqk>?G9Kpa`RoNv@VjT_#1~7 zzh+{D=;T<&B06il0VNpHO$>mXb~eW0FoE{^W!dl3OhU74qMhLci2wnp&kT{P*sQzY zX4YEOjH~LHimFGo>bic_tl6V3A;|61Qy-i5^8LZ>Yu^2&oWA*<$HvW@v})#CMYr>HMXD%p0O<60|Jp+vChfG}F?`{u+mm?&hNtkWP5)SPlycaw z_|a`cCyeZwk>aob5dWnUyEd%bUQlDTJF5c_cjw@lLwmQ6p+NPV-MVDyw#$)e_$sC8 z3kIcxYf`)1H-3Kl2Qz9Z)ry1wJZCSo$(cE(WX8%f_IR;QJVHoi4L|ywm0kDDnX{#g zwWn6QoiDDLYh5{Y@$rCHcXk;u>B+lpPIhnzAb9b}7kOo=z5CqNF`kD|$-zy_R_|;fGFUD@wDPYj zjv1W>j~jWvQl!}5~>sNnyqLR09q@G5D_<{-i zh_g#sd1Z{F_VR3+peWJqVqOi$p^)bBcv4al8V{mk!K724c)+rC!MxAPf#s#5_i`(Q zSDqWvcIx{@HYcYN3pQi3`^6il{=WH~mKbjjdnhkh06;d7QgUqF(p^;nY;D_j?C7zR zD$g&J~0KxHOZDnhGxy zpZlNQy?chsf0{9+sgAW+v3&8tgNN6x-_W?p&V}wRvC~iiLPT6mVwC@ z^E|@XFag`r(nY8!xX7^Bzzrz!ln@;!S_%u3g`RsjM8pjH%om+xY1 z)`D-(cxJ!$$Kid~y|g8m5@&_bxl<>LE~;UCbOJGHNsZFI)FzoVzegAo zLe-H7o&Rjnhi%^+Ghu*h!G@Ch_!7|%M-CoV7y+o2=S~;82z$ABb=EyoVovTka6#q$ z1((@mJud-L-vJjVLfEO1SgI7$wMPA*=SHvQQ#e_fe<`m23AcI-V-=5;y}F;4tL zcAM(0^WRukBH0D~VzHIsXoL`eT6XH&GcL|5Qoxwh%_Fi?tGCWy@_7kwl?qNT4kqPJ z9C7cuZ$5AO!i)*QO}h(4nnF@ibhPkzypyNCIAz)lx5pFJl$e;%lge zzF<(-i5c{#-x{Y^RI11{31C715DF`eqIx4wkg|2Wck;sAl^=bu{-Z_XIs#%XG}KRn zp^0RnD1n?fQU=MH=`1oY{&QnK({JYMuT044o+bu@Izw<{N;Qo{QYtDnWSXQtasU8D zVeh_o|8~@oGkI92x^w5%pdCKqG}vqreHlb(&StnP10cm&X)8?-jjP=n2o4bl;Al!M zs6>bvBbdHZpU)UOW#+OyrSW}Vc=hc?Pv2?!DhsYN9>X9R zX@zU^svEG!7dLM%3R5B^B8d~Q+W2Gdt=LWXpY&8$TZF780fwb85F8;hX&8Bc5Mt^k zAO-+~i2<--0s?{nqA9hc5+Q^T)6Ns8lP*7f;Dc9ZJ-_^9Oy9BNx{2X~Dv8t|qV=hp zPMz7+>|A@MkCd|I1-)K(al73I4;{XzPw%X*T@D;PXW!`o0Ns zmb^LhkGFd_%zxvM+mvaJ5JD3LnU{9_E6Cpd*uZqQ#urc&RaSL>Kvn&fOloEtlzjQ= zZdYk}`Nazt+|jpJ&47?%U`6lHEg7m@Wg3z(;y5#S;pnQ|KTlbEIic^voq5EVS|dt} zUfJ|rsSE(vUwrt(xv$qzikdu!f)rKt@seG7uMrQ04?G9}hbjXQb90iZQid9=-=Mge zP!m4Z)B;7xbxoBBAwiw~gP+@PPkzVInG%|3r{=zhqq#7V^*B*Z$ z`0zsm0{+0<1+VPc`_*HQ4vI8reh}4WV+*#tH+g-~B#2?C1S@TF(p51{K@`uf`EXz7 z@k8dneDjw(4;EetV&0k7AyfQz)dANP$bN9h@5*--=&( zuG>SSv#7Hl?)A{1NuByQ`sAHMjp zi#0NciJkAuX$QW%lQp3D*6pZVkUeE8*|_7>6@`mQFbWTxCUqPzAvDNXFaG4qveyPr zoDlqEd!d@xebCcg%;U@RWEMbG+ySs<-kp_FakR9y+%@G&u~#-2zVV`72Y@K<(lh%j42Gf`B&Mis4pBd^pOV3V-y_~JaLn)HH~`RVit{(U zwQlD{*=CI^{`9qZ{t=@Fj-37o4TkUB7mN4jD-j$B0n;wgC7a)vr#}AFL*r%*r?kp* zdG9RTeAL5=Rz$<$)!TYM|G+atj-OakP*B%liIg$7cTGI zwfocc8$+@h{?@2zy4@y>7&`RcduuzoT<70->+QyaTt5tA355b!(+!LP08x}=0ZFua zG%!Pc0}}v12=N@)#8_XD&tA0P(F?ibmY$IbAd0pNAn>9A5HtimP-&P10BD+u6%km( zYX{GUAcXPzeI_A^4yo?_fsT2_TLlcn*sLZ11~w238H$1d01!=6@m4SykpLI))F7JJ zZB`5rG9<-Yk?LTS@JlAYVj4zj46pvkSFy6m>Or4jgp~k>rW_)oSdk#Wgbu2jA$#|fiHjjXHrc=g)7Uwz%bf8V;VMJ*3f$^tl~hL!dEl4HRt z)heQ60stZv0(-qZ27rHum~OKQ0H8(mWk9w%Bmk)OUC?X-1A?`lSnabEt6c;D6B7Us zsDR*<}8K46B6#OT?^k0}l0*;wj*zsO|tnq%^3V z8jdJ8^J%E%WOU->$FhYfkOPw%_yDhtjQZr_o2;^e7rSzW_5*UV@?6-a}#e6#PW zTk$iY>7y=1*G9{flC zt*8TY#Tt!@1sfO@kwuym zrEP;l3B`qXhIljUJ)rA9-LBn32R z)Pljlg#sr(KMyX4ZNHycQ==;=EcNkn3k&h*ayba0rWcbGrBGIDG=$kq0$>~tQzR0& zxri~0R_tE})^@aJFc=?}DROd)+XwoxSj_rI=-G0dLzCdI0wMIz=M71cS{>;k7L=CB?Ykg` zhljgF;?iud1OQx~V97^|_$-09r-Gg$ut-ZKmWWhzo4LQe_%ks z)dJ%LS z_;O6t-%5; zzChyXCgwDJR??`DQ60z3`y{qcmiG&IJZMZPo<@lnx zyDEetn_(DUum8B~_0lir@5nblAr~LZ8${}=8i?d##|T8~RP31lto^^C(O!H2k|@tf zJF(|LYNffA^nw5(gzU>tMjF6u`%NYjYEq3}udm&OIjjKy02!1u!k7R1aeUwQB1(~c zZr6t0f0#rrtU6^Qqs}FE?S^Tgy4=gB_9RzmF(;3}P61wQib6(pMM)sfDsQA2q_saEXQUc$i<0I4gmPtcsbY=<|9FdEkm#C?GK!`FZYn)%1vm~Kk2Qej=p53{A*G;3y zm2DFVaiSJ(T=W8Q^VsgBA~nUA1O~h5jU+5oh#c!-zKgM+N(DoBDhHuR1`PWxBiR$Qo<(6 z^$S+r`di}Ky_uANg<*oI>GLKI=+w@I4SQ74j>87gT>!1UGA z3uo;p5{g)ut^FZ{D8veW@9>`JvmY=0t(*i5xLsGJ>~sEM?&>rR!koL#STJEgsGpdF z0cZUhd?|Li*AxJJ;?^*L9lk`O2Zu!ERgt zW8sl6RwWnLs0i0V?=PP`FhtBSmft$EZNpFZ%{klf^R2O@;fs5FbBhY32N7=T)c?Yj|CzzsCLTtL2A^OaPE2&8}d6IW0b9;od^4NMuc^ z0(jkrcf~*dt(YYciv>b!yhoCvG3&QZbH8`;r%l;PaCXIu)D}z}(!Y26s^qP^q#EF} zRdTb%A{w({%QUx)qk+u6II>=;P4S*qf^E(7Ur{_U#j6 zJ$3QIInDOO)}!BUT6^tATGHX>$52dhkf$-{aFKzs>Iv1{zh254;p>DKRZ{=}3_-g9 zCn&#HtfipI*qu`6oJ+b!=u9Mq-B5K-`lM$7JGEMm004{?_ikkrm;vtI%~md0GiB<_ z$)BCoi+#mFTu%AzOg0Feot^m%yg8A)o9uN8s22$UNB{(g$UD4gU;AaVKOHsu{ZmE+ z$f{AG>d1l`$V}Dp$55Gl=U#ysvL9H&d(U&ddG7K}HG<4y6xHk9Ucvm7w3>!CVY;;U zHsu~JG>{0xFbq&l_O(a6SYH8NSVaKN@re^$}{OkVYE9ERNPXUu1 zHhDsO*@dz zcS%6mmmB-SdWrEm^HUU}C{zo|!(C!eE`(53wR+KqA20ss6IE4pBO-c8B*+2GT7{Y& z7>KHsgwbd;XcRTvP!FS0r6+6egfjsl%4D)Mxt20&$>4FHZ29@b@8>U``DsA}0tO35 z2vM~%TmM+9?p`fMK2d&bFckfMOv)OzX7APy5@Otq<&}h)YDT2$COh?ZBGumjnJdn3 z-SLO(q)#V$8B8QXwT!Vg$@=z*0P3_?-)s?OsH)T;CwC_dAqr6jmCA&8Vh%LEyG5%| za{_~qT16NQ27^XX!|UW_QdZfCWU9Q9`0+clmu*c^wj2BT&ZE2MbOQs)@Qmun(PpJT zG&HEszFi}P5Yr5Q;W7zZbY7;u0{h%)$X+Z zaF3D8zZl=X>~!LqPnWLPeDr}$K+1YUppf-U(xTUs2yg~YQKpBd*UnqK?qI(2OA9vc z`*BtTi=rA;OI6=Kk|M92sj|vmbka_wdTme$kyUBCw_Rq9S~4}1gVmaX!Vrlt99gK^ z28R#;WoNQFIvC9eV_>#z(rg9{1F%=PRZF)Hr*fvOT|Aj=#2n?GqCzN#*S|2 z&q+ho^~Z>EMAxdSs_W@#eS7zg=p26SdRnzwjqJ4!sIg({v`=ZX_pSWq3O{zq);+tw zp5ISX@om6UfzHNK_MqRYSZ6g7lauO$eL&eDW4$fU@7WOj;-c2cRdTAfap-v%E%d@i$;pt$bsqq;a}BxgR5q1Z96_boYb zzsk&a6Edt58dlx&yT!%7bn1^{lSl9PSv|3wy%$Q22zQAd)WMveTUmp-ga(U|u~2WJ zni(FNl^KWEXEY;{-H_ADFaQA1-aqvH>F5uKNNjvC%QDg*#f^P;{^+eqnF_P8gBt@# z3@8dA4B0NH5W;NEr*;gBpw0jk!Y~Xq6zTTCHMb8ozo^Lyid!<2FHOrRkZF;VU(eTO zgn{~8w$i9kCY_Zno3M5zY&(!vs^_|UVMSS`s8RTeG#Q6Zl`bE@>SOIUzuwjR4xBc( z7kPWj?gZs3L!8*I=S&X{+r_x-d{Nx02!)Fvu-HO zh@udVa~KF?vw%Vr0F%MS0p~Jpfh3!QBL<6Mjhf(3=7Q}`z?ck!!F53Vyw}~h7=Wp& z=x*}HJxSR*gdk_{rzfEENa~>JEOu#uTJ5w794t%~sKYddCr(e&& zOp5H3txNV@R#*@KrM$lXXy12UT|6`Ey>*#(f_`|q)>uA^_vfj?b)`iHzkVXtYX-Qq(7wb^1e zfXvK$S=qUxV@LGq-Lvkp?E87=&ZpeHCyj~-cN7&pO)%LEgt1t4)80>2T~GF&g9Ae& z1X!a}+XQc#Sw=ujS{cq@Sa*ARxPP>0en4P={aJ5}jg9HqRpRDm-NbOb<)mo^c9}|( z7f(rk8odbCYV@+F5r#EB(1-<&X@S|D@H|OFHA5O?=W74};N+>(krACSz^skA>P~ed zwIGD5RMmNT1qe`PRuT8h9G(R73;D zAOHZVG+QdocF>5PwsoIpNAqM{tX=PK%Fw#L9|Hh{)$ecUTo3?;kR$S8&t2P1*Pf_m z0^w#FA9@mNv>{UcMfZd?eM0n@;gXd-4T;i(H_$w9YW)&|=jCe}kVYhOHX6-9;~9d@ zW?#H?rR~bkBnZOGQ$ic^S&m&R<;phw2(}C;lZkdV&j>6gv#mb@PFs-%O&S_BX`Y1+ zMBNf*&l?lSrd2_crp1G5o^R*y5O)s!?+pH0Al%AUV0%Ol89BUPLsoLOTX{RsnCIwM__2608zAE zY0@|VJ8;ySu>n=*H>}z#)!HTzZ8P;zVnC`3t{(ky|Ftp`0$kKB;mvW~ z13aDC0Ej5Ncj?%{qkqUu2ms^wy%0BTRPP`W4kmTpneWymmzY}A>k2{#$Hx!p7U+x{ z<@YZoA4p0owsB3-YH6B38`5xt zhV*wMr9UL!*EU%{plS-P9ZpP9X^|kH|Ad6e?>2 zqu%khl@kB}V3z0H&df0aWM<^37kxf?^b6j%jw_s8 zoIsy1&B`g%A}DHU|L7DpW{9`y?CLGa*8IE&I``!>#|-fKBl!_KY|IcJ)$gmnIat&v zO9&Xk=mp4~NJ?ic>IE}uJp@p`Vx)Pm(tmV2l_Tc4XzZme~1d3VZTd`CY% zR_Pk*&(`POEpPggD7X8xw?Kbw(+}s%>B;eDJLD#{6*3b@g1vc7*ueA|JZIK0ztVGw zYgaB^m3Z=@!%7f^Y_GN6$2mn(y&#j~BKF%+iU(y}5)4GwHf8;)0~H3#!$&2BCDo*(7`A<+2dExY z7!UveWt2%T?c4a-lB9CCzC)u#e?FOFmghaP@Is=zZIgAluw%40DDzA8$gFr!M)E=; zy_?SCnJcd)uH2SN_IzvJm=HcaW%Jk|#LLS=AmF#&KZCU>AIK%W1`Zn3J&+4HSLo*H zAKI<|D-o8Qd`9;dy9Mzy@*3cY+=6=a?F@HMr#>`bg3y5jBmKl|6UA`y2!APIQjGXP z^6pdlI@~RKP>c_k!EuuKhIfmJkjPShORw)eFiyMvgS!h=4=X6Khr6eTyQhb{2q#pk z%5rwsVZ-|Pp=vEd5XUuP#ZiHrKuNXy$YrI(djIi>i5;c3{Ka6gdgN5e4)Z z@yd8>5>tir#I~O^nvTib2E6&-0d-rNi)$9`DJ)9fu+|tKKX}^w(YQ&Ocj?D92d`I= z0D$y)2iLA8;>Nuk|K3mrked8c-`>ovAH`cLFKpi#wqoYw$+ru>yQ!f^P|f%MUAjbg zc}kw=s}T&Z*kwzHmu#H3J;(Szpm7$HSt(a$WZa`;8X9yUOoPU=jA{P*Fumf~l3B-S zQ_!A<22C0oG-%S$ph-i6CJhamG&E?^(4a{}>wr#Sp-J;sL8q`hXVPH8J|Aq^v1(!$ zXFC%xykfuDvhBTa?$fx}r!TtE%>Ord8cF~6W)BNs(@po^iKnynq|SZlHEPbp(q+5u zHhw3C-C^pN3&T@ae4O;i3;?{yg&W^-`uU?TE>=;vsN47%zMX<4iW6IY z$kVZX`ozVJok(P!Oinzm!hQNA#3js<5)mw53vP-!=dQ6WD5*{n9`ZH_NDJ*Sw^kk{Lu;a%& zq7iS84HG;;aDz;0g%d)5aIZ9!9 zk$?SrI)&x=4!No7wB0*W83W#m>ug6Gib8-hTX0{}YyrGBodi)960ny2n?gE;rA^P% zAVRu-`$coF*9Upl8Aqqkf~cJb@2TCHPGM==qya#>+~n`isp^eDDl^i`8J!Z|jP2be zJS-w2So}9d;QMsy93B~A`-OP%fXF(0rowgb+i#ET5fK(OFk#lzaPn^Qg;FyhA}cvX z;X35q|Gx1;xA4%;U1R!&iRv7SMSW-Y`UCgf2F;!rEu!aso_!!}M;m?K(cKq&%9n;XDL6e3CO&S_B zX=u=-p+S>|1`V1tG-%S$ph-i6CQa*0DvEw%#>~M!tVZRha3eaS4-s z-zBXE9=l3meq8p+lQWslX!%EzhLopnP4sT*Z%N|N#H)qUo?npzo(4?xr|A7KJqoqyF(^!<* z85O{E_ZCu)b4MDuDaY9-wes3o8E+8}>5P_EM=O7Yh3!A=KXF|>_*}lw#Va6ez}PW8 zB!)kdPu;4JKj!l%r*G9p4JK8EgAb@6V0-s@xraAZmQz@+0k8UU?T5qK$VXZJ1z%xl zUD9AIkynSv?)?V7G_YSx?;f2bx}0hASi zGPn_GfUM5FooDgsIcWH>7kh;{%P;@3ZTEG#832$$E@uVz7|`D%_gvb;^15=m++t4} z-+*p0F@1X5eq9(D=kF?&j_QOzk;<$smdgDHk9x_c?Apz;)}hV6fHZUp%d-ZZ!a{=% zglW*ANkijlz&0byX0vErJR2YiwRzGA1OkmlOWX2ULM#%|N}xf51`QfCXwaZRg9Z&6 iG-%MEL4(G#$NvNKjFRTab*<0<0000b$g8Gd)QEA8s^CE3RIWNZT|N5N&%l5iAE3WmgEg0W$sA#s^$+JSNwVrWzD0%b}d z38lk8NTvj63dUqgDbxmI2o8`j0b>lYo)!#Nqwva z{||`Pi3J5OQxxU#c>eOyVWZL56VJ@dEO~7K%d)Q&Z&dm=i&)sPJzHY98UAv|wx zF-_AImFH_~YyDFeiv?q1=#asQ3GwHvs=m4i5mPpstFyymvN$YOm&4_ifxyTWXdomQ z%S>$@kFDNhl}seau2En`XG^QiBj}U^WAnkq%gh}nQ>Wc*by{qkAn-a?E&&l#7O@j4 ziegz70F1`w(3&Qr5dc`0r6@`i#b{##nbpp5PP-t1oS_Ek6j;vs7x0L&TKExp^Ipjv zJyg#UAle@_eSW_D^wo!yN-On`3S*hM!}Ei;_AiaQuVeQL9N6{0G8!J6e0hUr-VCK5zfoagz_8aK!F z6h>JP5T?zYTt;s3f*CoPT4ZXvT7KxnJ+~b*8ef5k6?Ut<;J53GvYZW-r}s6qItfi` z_OKy3-~~+jQ3q2sUM)X-;-0IE3i~QCfPQl6YV(>c6-*-O0(pRCfkLBe^GdA8t6%bm z*MI1!E!$sav`H#M=5T|PmoR|=L%@(J6u|*6-_>6tl004^C@Tm;cUi=Z1!KZu7Y5B* zwecseGe`FSwN3WS#941_TK3V}T}^_AqZk0vYUNT3mlb81&uravq{*vf6#&HT8`r92 zqE4$5aS+w&Ed{B*$e{1PxOj8M2c=z9UG@+F*1Px83fGoAEIoKfo22Zu4P%4>^;;V! zU`WXs#(1vmJaFnhQpj1+*mzy=Cd6w+AObxE5{E-e>tnaO%N;h_D|cGk?o}Sx@%GKMqS;v{yNASBKyoA*@+T*Ww~m*8$r}>1 z469&Rxh_tr)@Y?5Dl%(+erkYrdgz?OOdnNmmvz>>xBZ{)A1_*-8}G20dOd`JX`@L3 zTOPCnGh*u4cmZ*4x0*(}IE@%YgfYgL!r@wR>7rkbAOF1X`)^8DM(Q;H*td6gP0f{$ zj~x$9i2?>)x?cRx$reekQ*%IYDy6ZuT|PZUE;SPfi4q`9dWI6(8y-+J5>do;K@>2= zr6-fYaaCFhE&ZE72xGG6Vt4b&?H^|CELoCg+;aS3cNhcq?x+E)Df&6TT4Emmt#%3#okC6N&r+gh!3 z`m;%--Q^MlEV&#R!xM$}4lf}|7!bw)1OWgTV)j^?rVV2$j+N}UJ}F(ea_yd~mZV9`x4pCdXU};ZP6|mu^1_=K+6!m@ zRpE4THoLR3@_cAEED1$$1pnpHYFT0F8!rq_qZA2a3Ky>&XZ?G5W1NZ>F(wHFuGTg$ z7@kcNcpwmpWFkdy1mAeHvNPbW=s|gTlaC5x+`HIgP-~dl1N+X)a+kd}N)t|FpV~0S z7)x3P$H&XsZeRXj+grcdTNgKJ;m=1aMXxhhURcY5V6j+gKD#n;!g#CIdb#GasZ*!K z=>lE^Bald1mAv`LjvdbVB}>*XV3Fhgwd0%jUbjeyVF6yyXQu&oBI-g z)Y9!@zqJ8?TVw=xr-GKzB!yXpx&Dh5$T4JS_!|yN9&kG<@$IRM1r~u}bCLBOg|eSUh|4#%KOf zQPcR?g_!vCA;VN(96NajSbra*Q3ZO45ymRYV{1P3?wOoT(-S4|?amnWTN@`bIBD$S zS5?ionjV-rP#Q+OSeOm=$~rTnka+{g+jl2=Jf4~>SI3PTo0t%vm=Hf|-!-BfL}31y5#85zS7Ni3TMukP`oss2EZQE{klu_G!SAiaYQQr z+1#y1QmK{A$KS6Q`{v}XSg6Vrd?ZxZ(jvFzO&CB5&H{ z&~dL?v25_*Y~S~abLandtjx`Meg8!8yr9*vbBl_mOzFDNXl(xDu6H7O*bj(U?shv7 zFNg>M06~%}IjB`iG2{g+3NWS|4!eZ0L8t0D)(VL6*i1k{oJK80yE+Uwi56UTQ51dW z7fMFzlmtmDafqn!M;t$8xVrjNg@S#3!K*!yLx+!ExOgcycM{P(*P@g32^n}bEDbE} z9~2M(42V|Hs96Bu{lrkfsdXv<5C^W1A$Ug31O*Ysp`!Yj&Nu7oZr^Dvo;`EI_!oMD z4R;&QoUOcBcPnS~C|{&MyVG|V;Wrji)^(mtO-bIgeoaM8Ee z?(`i7F(r3GXmsSr;bU?}L%)_JeEr7F`uc{iUD;CvB6srmZ(74skA5AtV~8P!7-EPa ih8SXqA%+;f5&Re2@;!B(Q!B&(0000