From c39eb7f4926ddeda983706b1d06d39d289352d56 Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Wed, 24 Feb 2021 16:37:21 -0600 Subject: [PATCH] why the heck couldn't I write a 12 page paper in a single day back in college? I would have killed for that power --- .../#653 - Quake Mode/#653 - Quake Mode.md | 165 +++++++++++++----- doc/specs/#653 - Quake Mode/tray-icon-000.png | Bin 0 -> 19929 bytes 2 files changed, 123 insertions(+), 42 deletions(-) create mode 100644 doc/specs/#653 - Quake Mode/tray-icon-000.png diff --git a/doc/specs/#653 - Quake Mode/#653 - Quake Mode.md b/doc/specs/#653 - Quake Mode/#653 - Quake Mode.md index 5573b631276..9240b7ed054 100644 --- a/doc/specs/#653 - Quake Mode/#653 - Quake Mode.md +++ b/doc/specs/#653 - Quake Mode/#653 - Quake Mode.md @@ -1,7 +1,7 @@ --- author: Mike Griese @zadjii-msft created on: 2021-02-23 -last updated: 2021-02-23 +last updated: 2021-02-24 issue id: #653 --- @@ -27,18 +27,19 @@ background on Monarch and Peasant processes. ### Inspiration -[comment]: # Are there existing precedents for this type of configuration? These may be in the Terminal, or in other applications +For an example of the original Quake console in action, take a look at the +following video (noisy video warning): [Quake 3 sample]. Additionally, plenty of +existing terminal emulators support similar functionality: -For an example of the original Quake console in action, take a look at the following video (noisy video warning): [Quake 3 sample]. - -* guake implements this as... TODO -* yakuake implements this by ... TODO - -Both these terminals are listed in #653 with descriptions of their specifics. I should include them here. +* **Tilda** allows the user to specify different keys to summon the window on + different monitors. +* **Guake** alternatively allows the user to either sumon the terminal window to + a specific monitor, or whichever monitor the mouse is on. Guake only allows + one single instance, so pressing the global hotkey always summons the same + instance. ### User Stories - The original quake mode thread (#653) is absolutely _filled_ with variations on how users want to be able to summon their terminal windows. These include, but are not limited to: @@ -57,25 +58,6 @@ are not limited to: * **Story G** Terminal doesn't appear in alt+tab view, press a hotkey to activate the single terminal window / the nearest terminal window (I'm not sure this is distinct from the above) -* **Story H** Press a hotkey to activate the "nearest" terminal window. - - TODO: I'm not totally sure that anyone wants this one tbh. - -[TODO]: # todo ^ - - -### Future Considerations - -I don't believe there are any other related features that are planned that -aren't already included in this spec. - -[TODO]: # Which is the "current" monitor? The one with the mouse or the one with the active window? Users disagree! Users want it configurable! Gah! Do we make it `onMonitor: onCurrent|onCurrentWindow|forCurent|` - - -#### Hide from alt-tab - -[TODO]: # TODO - -This is another request that's all over [#653]. It's unclear if this is the same as minimize to tray. I'm not sure that there's anyone who wants the terminal to `minimizeToTray`, but then _not_ hide from the alt+tab list. ## Solution Design @@ -98,7 +80,15 @@ the OS. Whenever that hotkey is pressed, our window message loop will recieve a arguments for that hotkey. Then we'll use those arguments to control which window is invoked, where, and how the window behaves. -Since `RegisterHotKey` can only be used to +Since `RegisterHotKey` can only be used to register a hotkey _once_ with the OS, +we'll need to make sure it's only ever set up by the Monarch process. We know +that there will only ever be one Monarch for the Terminal at a time, so it's the +perfect process to have the responsibility of managing the global hotkey. + +The Monarch will be responsible for calling `RegisterHotKey`, and processing the +`WM_HOTKEY` messages. It will then dispatch method calls to the appropriate +window to summon it. When a Monarch dies and a new process becomes the Monarch, +the new Monarch will re-register for the hotkeys. #### Where in the settings? @@ -425,23 +415,45 @@ the following property: ### Minimize to Tray -[TODO]: # TODO: add samples of how it's implemented -[TODO]: # TODO: Find a sample for hiding the window from the taskbar (when minimize to tray is active) +Many users have requested that the terminal additionally supports minimizing the +window "to the tray icon". This is a bit like when you close the Teams window, +but Teams is actually still running in the system tray, or the "notification +area". + +![The Teams tray icon](tray-icon-000.png) + +_fig 1: an example of the Teams tray icon in the notification area_. + +When users want to be able to "minimize to the tray", they want: +* The window to no longer appear on the taskbar +* The window to no longer appear in the alt-tab order + +When minimized to the tray, it's almost as if there's no window for the Terminal +at all. This can be combined with the global hotkey to quickly restore the window. -The tray icon could include a list of all the active windows. Maybe we'll have -two items on the tray icon: +The tray icon could be used for a variety of purposes. As a simple start, we +could include the following three options: ``` Focus Terminal --- Windows > Window 1 - Window 2 - "This-window-does-have-a-name" +--- +Quit ``` Just clicking on the icon would summon the recent terminal window. Right -clicking would show the menu with "Focus Terminal" and "Windows" in it, and -"Windows" would have nested entries for each Terminal window. Clicking those -would summon them. +clicking would show the menu with "Focus Terminal", "Windows" and "Quit" in it, and +"Windows" would have nested entries for each Terminal window. + +* "Focus Terminal" would do just that - summon the most recent terminal window, + wherever it is. +* "Windows" would have nested popups for each open Terminal window. Each of + these nested entries would display the name and ID of the window. Clicking + them would summon that window (wherever it may be) +* "Quit" would be akin to quit in browsers - close all open windows + [[1]](#footnote-1). The tray notification would be visible always when the user has `"minimizeToTray": true` set in their settings. If the user has that set to @@ -452,6 +464,11 @@ There's not a combination of settings where the Terminal is "minimized to the tray", and there's _no tray icon visible_. We don't want to let users get into a state where the Terminal is running, but is totally hidden from their control. +From a technical standpoint, the tray icon is managed similar to the global +hotkey. The Monarch process is responsible for setting it up, and processing the +messages. When a Monarch dies and a new process becomes the Monarch, then it +will re-create the tray icon. + ## UI/UX Design To summarize, we're proposing the following set of settings: @@ -487,6 +504,44 @@ Action Center, Win+V for the cloud clipboard, etc. Users will now be able to use the win key themselves, but they should be aware that the OS has "first dibs" on any hotkeys involving the Windows key. + + + +Mixed elevation + + +Only one app at a time gets to register for global hotkeys. However, from the +Terminal's perspective, unelevated and elevated windows will act like different +apps. Each privilege level has its own Monarch. The two are unable to +communicate across the elevation boundary. + +This means that if the user often runs terminals in both contexts, then only one +will have the global hotkeys bound. The naïve implementation would have the +first elevation level "win" the keybindings. + +A different option would be to have elevated windows not register global hotkeys +_at all_. I don't believe that there's any sort of security implication for +having a global hotkey for an elevated window. + +A third option would be to have some sort of `"whenElevated": bool?` property +for global hotkeys. This would explicitly enable a given hotkey for unelevated +vs elevated windows. +* `"whenElevated": null`: behave as normal - the first context level to run wins +* `"whenElevated": true`: only register the hotkey when running elevated +* `"whenElevated": false`: only register the hotkey when running unelevated + + + + +OneCore / Windows 10X + + +I'm fairly certain that none of these APIs would work on Windows 10X at all. +These features would have to initially be disabled in a pure UWP version of the +Terminal, until we could find workarounds. Since the window layer is the one +responsible for the management of the hotkeys and the tray icon, we're not too +worried about this. + @@ -497,6 +552,13 @@ register that hotkey will fail. If that should happen, then we should display a warning dialog to the user indicating which hotkey will not work (because it's already used for something else). +Which is the "current" monitor? The one with the mouse or the one with the +active window? This isn't something that has an obvious answer. Guake implements +this feature where the "current monitor" is the one with the mouse on it. At +least for the first iterations of this action, that's what we'll use. + +`monitor: onCurrent|onCurrentWindow|toCurrent|` + ## Implementation plan @@ -524,24 +586,43 @@ work would be needed: * [ ] Add support for the `alwaysShowTrayIcon` setting +### Future Considerations + +I don't believe there are any other tracked requests that are planned that +aren't already included in this spec. + +* While writing this spec, I wondered if anyone would want something like + `"window": "name" int` as an arg to the `globalSummon` action. This would let + the user say "I always want to summon the window named `name` / the window + with the given ID". If that window doesn't exist, make one. It's an + interesting idea, and would override the MRU selection based on current + desktop/monitor. This hasn't been explicitly requested, so I'm not diving into + it too deeply. +* Should the tray icon's list of windows include window titles? Both the name + and title? Maybe something like `({name}|{id}): {title}`? I'd bet that most + people don't end up naming their windows. + ## Resources Docs on adding a system tray item: * https://docs.microsoft.com/en-us/windows/win32/shell/notification-area * https://www.codeproject.com/Articles/18783/Example-of-a-SysTray-App-in-Win32 -[comment]: # Be sure to add links to references, resources, footnotes, etc. - - - +Docs regarding hiding a window from the taskbar: +* https://docs.microsoft.com/en-us/previous-versions//bb776822(v=vs.85)?redirectedfrom=MSDN#managing-taskbar-buttons ### Footnotes -[1]: - +[1]: Quitting the terminal is different than closing the +windows one-by-one. Quiiting implies an atomic action, for closing all the +windows. Once [#766] lands, this will give us a chance to persist the state of +_all_ open windows. This will allow us to re-open with all the user's windows, +not just the one that happened to be closed last. [#653]: https://github.com/microsoft/terminal/issues/653 +[#766]: https://github.com/microsoft/terminal/issues/766 [#5727]: https://github.com/microsoft/terminal/issues/5727 + [Process Model 2.0 Spec]: https://github.com/microsoft/terminal/blob/main/doc/specs/%235000%20-%20Process%20Model%202.0/%235000%20-%20Process%20Model%202.0.md [Quake 3 sample]: https://youtu.be/ZmR6HQbuHPA?t=27 [`RegisterHotKey`]: https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-registerhotkey diff --git a/doc/specs/#653 - Quake Mode/tray-icon-000.png b/doc/specs/#653 - Quake Mode/tray-icon-000.png new file mode 100644 index 0000000000000000000000000000000000000000..3a2b8df90c7b7ab1795827f4c92a98dd14be14d5 GIT binary patch literal 19929 zcmbrlcUV*Hw=D{S0xF=OA|0hE2&nWL5EYRwQUiqELO^;635p*e(z~>P0@6YY5ITs3 z(0dJ1r1wsMgd{iq_PKlCz0Y~>KIh&)@+2$o%3J3gV~#mjBK37O7%y{NrlO)^)O_*u z6%`eA0Och@e~I!9@3fXV<&E0=mBtgQ@_w#$$_1^HnvNP3RaM-TbDN8lYlb&3%)F_n zn11|yQDZ!c?5U`9+%=!7y$-P2Zee(9WfVvz_a?$vzE=u9py{MyQ4``Te=kt~(3T}M z@`@=x8NHA?ZVl8VkKG+dYl5|~Uq!5eFMzc&HVjqI4Opj(Zl+v)Ntdi<^OOZ`)Ai4T zr1Z0yfrb-8tCPE9V%Mi$ufyQ^rL4oORi#yV|G?Qp#7Xvvv9`)VJ&!jR#`F0Nw%%Yf zV-BShvmLqE#T!?d_hJ|2F8-ovUX;^S+o#%yT?8ePPXPoBJKw*4eHQSB_r~8({~F3ExpL6mv~EZSX`V3#_1-Qa zO=l(inx4g>kxN{zl!@kjVA?FowwZ9OWw~NvY6lrkH40%3_+0PHSHY@fa(1PgFFkNJ z5ZBHe(%BKmo1>Yf9!3}7^m=I43LGPP%ds~3V$AXQEH+~-Yv|klNL*bUsa*qtJ#tC^ zDSv~qxaT*w#WeXUJZdhQDVx2VZVvYYPnmY~d$c{sc4FLKEAX3b zdXS{pe{G(6mfiP(hf8%M`_of<-`}R-vdkcjN$JclM(~3R`V-?UEmZl)DXhCRW7J98 z7spy2n&dlVareqziSbrk;f64+xMI$#&51x0-T*+{wq z#p=H8UYvd`3WcmEn>{QWN5V?W7^KlbPd+mnPmkj=P%&X$SM>1jR3oLeR{nuxA7x|y z^sPX5YX?Y+0BnD@B4vQ0zXQA+e=uba;qDusSWv1&{=#x{^vYDFwaQ*GM)shsWUvc( z<0irBJmzV@R0Oi)_Mi8!j~qY!5~I{!Tval4c>N@1aN(YFMPSr0ff-TSPaE?mw~uK-MQ?09@KD-OPfIsyL~vFjHAY6M(rW@eRU*} zf;DA&Ru4G{SAWuO3juScJ~@a2HzE5Zv{fp_@iwP>v1_uJa*i_98E!wt}V)xG9eS0q~H_1#<88mtVEcYD3U40S*7 z(I*4E{)FfAD-=60c!A2$Gw$(~^tulJBUQTXTsMxSn1~{!X{deJsKt2e%GWW+f)8Vv ziSx78hcAP)AXe^Cj$|adY}5#MU6kT)KHuun#o>!R$X*ao@XensU&iw!g z-i}#jR$-f{!*;i{ax`Ge8w#deGxKGCV7-A-G&W~nQpPQMA_|~!Dee2_C z6r(X?12c!4H*cDOJsYf7dQijj`!|~nEpC-pb~MU>_vd%GzEaNh^0SKF3W@T%@f7@c z^dcAh-%S7TR*WQmUo4}2QS`r((SMlv|HM<2CX#Z1Q`;JJFPL}AGn~cJpQL=+{)d|4 z4x|X({x=KM@IA@^|6jQD|J4H|8%^uK?_lePWRiy!nrv*wWo}p2QOvNjB-T*iFNZ}G zl)C*`FrStJr}i0}&$-tgn?XYYuK6<={%!rIzz>`>g*S_G3lw5_0JPt=Oikg&tpDXP zpGu$bvD(@rrX6S%g(ppBTN`}uf3ZJqt#5aA<#4OACxrSl5Z%ZROsmRA{U$!%^xNz( z@`E^QS_R|R7v+9L{^i0-Yx6SSnmWhY*>)L5EbU2Yulo?LFR1#f09A#Rt+$B`P(!1- zOnZr7vCaDXN7FyO9o@#Ipsb7g`4CTaXz})~(z?b$1`tOXI?zF8hpWlacYI#%NA$nC zb_6$#JX`#N}Q2o=|1GdRGe$P^z#mbYLCmKymr@VX0-7eXy4!IX*JaC?4m zfyK%?VG^9uPgHbY0qu)YAe_YbLdH?OZh=~v zW74S@N8odyl6`e5R))_}4#AZ?#Cc7j?NZY?Td|*@B;hF)L1G zblt110<=x8jiXiSm9iHFW*JCKA%TAA+)SC01 zbJju6u88mNsw?nwQtrb=Q5N_k(qrf<52*crGtT>OY_pRn)T@g4@>7p$E`_M81UTE& zv+BOb-9xdHdroa(dJS`PGyc#2l8}LV0MjPLOm8UH7CT@EFMb(1S@^a)e?tD}KKyz$ zOc~>uAhtSHyJVJWc=g{5S4KkVsL}tLdF#)RxEW9UkH05qs_KzjV1XNxbu&I@R1zVKJcX$|J)Z0z0l9r?HP|} zuHIv-s6tZ*=DVOVLYYRz*&sR&k{+N0@h#9BNc^3nuK4ON=w801U0NH98%LsL?^sXa zhsXQ6$Af@bS6o}})E~YOf}9{_dk~nHxzUjQrd=fuUlxeD&;FrAyVHhDIXROaIR;0&GUH00<#HKzprZbzGhe#Tn^UP0EZF!3n9P=@Q; z;Lj(AgS{Y+qGj_0xxqMO%D8!m5&eo%5}M*8LVy5fS$`(+zdK6J{Ik5cY``_}e?b1l z5@{_NGvG|LmFV2;D6q`9&gN!!wFSjdzK4x}x;oN~Cg;}4mO{Xa=%9+46OC#7f#1kM z?H`w!f}1`Xhhhb}W{4ir>eNs(j&CPV(hA+pjJXHX_~t^tnrE|rkD$G<_pd|v>qf9k zTkY|*J#K;)R*@{H(~wQ-@)u^;2M}YnlJx%&-F%~UQT|jzo|l=$KJv`DF400V}QbA2qY95lck%n%{QHnN}oL_0B4S z3#5g2nK)RhDa2zetL~SIjc!DKGH|bMLF}8emaX)7v-c{6u%i5h4)K6n&6QVHXsb#*pld;)7gJ@_TkZ&K1X{96tGyE*D_w-ja_Kqnp85G{%X8= zs6PHKkynrRG`s@&T;=z|Kp}U+siavpkrIq4pRWDrshml8KOdMfv%q}ZzvqNv`Y--h z=bS}c07_9v1Ss_$WAgt;m;JlHM)NGJB32NF3ikq-*4rx=<@hP8u7B;V^>6!S;fG~z zbuI*Aq&K`!8IIL2Zw-p<%Qk|$Ar^1M7+jk_6O+>Wk@=kJxYn2z&~T5Te`0GhkcXnZ zMnOrv4N@1Bm9pgO{$yR>11D;2kH_fz(Iml?>EF{iOF6{-+@x1XMUDr)@DZs-(;*vh z$fDL+$BJx6e@h3yo-9FhxIevSpdq+L`l2A>S+}IQf#b3SFtWK}hQk%;CZqStcDhKZ zvg{{Ch%m8`z=`jjXv^pGoOP?>Om|(JM>_dnvo2s5{zJ>D3n@t96<*)jryRw3>)=Ie z%J}aDWvQScI`3nZRI)VVxN{w?qLS0=fx9h!Ml8Rlli2>TCZ{gV{KHe8K}+hu9;g>9 z*DLZzU%xS*_hwk*5sQ3;7%e1pJ*Zmf;#rtX+4!(iK{zwW%w@t90$Y1yEgT-p{GROJ`AFBXU zB5wL+;Dg53=Zh#5<~NNUSTqQ{cDiLICi9*xt%av!Tkb~>J^h}+(*YETrpa8H5yCbfoUVsiHsjqYari#b=(YS__oRN}n(^lMkq5Dj=O#8nz>n$>B% ze<8ktIeIy290*l!JZZC8G;%D%e);pmB&K0<1L)DElI)w6(EczaN{C&76Xqd&x9efh zngF%uN)Is|d7Ywis!O+0RoeO%xq{Cr2JCd3CV01#N6{1SQfM#eB=FGXz@B+e@U+n} zuQsjq$@*XE&5XEyeIBYfm_&POxE;2N=-cRjBT39}{Gsrz+7@Jizn~2I4EV&mpg{OR zb3V^LO9hJEXLsqMIdA{-$u6$zWgGk0q7X&9u*>}I%LX4W%{$DGW1-|VB$z2jCZZQp+8iXB536zC&)ZPt_c0FUKlEmbcROGH z(8K3}w$Umd5bNe3kusKhVlkDja`K(aGQz}4Ux4q(@pR7+5@SpM| z*Z1@faVOKs&qACvBnT+(L;p=AzW1=KsR`5A6oiaOPWIOWV6YXg>!uvJ z8+o$#(50I`&?Eu*t>SXKrJ8~uY*HhS4vblUZ-bZavhO@1({}}JMdQ=XJKBWg{eGKf zDmv-XZ#|M|uIw8ZEUhcd2~tS9%01H7EKj@7v!ZD|aDZ^H&LQs=zeb#^_X9eM^~kI8 z#qVgARN*>a7JW@+j@{{IWwHZ+!?Yh{3TT*2g|myvC_+CP_STllnA;IvW;PWM?KF08 zrOIo$F*3UK6j_OQpgMe&J}LSv*M$&j)}y)3Cj{v9?wIV+o`hYwwRQ8i%}dl5P~|-2 z2P(sKR^>;1xE)e=5 z#>7p6Tkj&EstO3Vy{VaP4S0NVmvRsw7H;;;j;sowaU4 zgbeZkD`(I-DB%(9&#}$k2iG7! z^h|b>I#I_X*t?TiaA2%$3=1MzmCl17YnE(nRVKCJn`{aY%sX$)U`eo5ley~toEGPi zR+UFdOj$H%xB3h)AtIt?lkan?_kd=Qe}bdF0#5yU7sBle`Xj!252rL6-usJ;g%3qf zR|q@R#fF7gGpD}Fi8j))-^TsIBhKG8_#+p;?+h5Akyq8$?<)Qz9&;f;V|#~>QUdMC zo)J6e$#04G%@y;c+3Go$x&bm<(^~!nZVG=)}2v@PN+tqw{5F8fGAM|6t} zZ2P(6W|>pVW3FIzYG!=*TcLy!h4tM_I&_N8?2UOIaT!7Lx3LY9rHs+3skfl>@94h6 z73X|;@72xtH4(co0QV;U0-$t6#zywQ^gqHOw$pJTMz7%Gt~}HB1Fg0u&Y23N57#DF zCuGCs;hRMA^(B46qNkb5ymp4vebMcl@)1&^Vuhr~&wRKR6I8j?SIMq_kJQi9B#Ndin9W8XU+ zGkLzrIeKw?XYT#L$e85Vtk1fbW-*3Dm^O+1tsGFecgk}R%Ja{Z!~b=-SJFKk3c$;Z{LCeBoIW}qfzLtauyhuq1MOmmQ?lw_tJj3b8@xGLNn#4mo(QhZuOia>z{dUmVihkj3mv(kGm{#5SeB7qp z7lpMe3lS@_XCs`op}`eU^r>u>x|T`p;VysWaV$nl<@H4ivFblctKKb|I5~t2o2(z?;*?SUZ6&7?a}X0(!o>05s$yos9CP7&U;2C#eH&R zRz8oC%BohlB40)kDXp3kkv~>5_5eO!;Wv$6H9M4-l)MB`DSo?wGg1!ZuzpGEDpzv) zTMA&h44a833a%^0dOQE;Y6LlcjQ!SEZRKG&P9Ka<5CO{GDHKe8>41^9*k?AQB>S`A z|M>_1efi?w!{q-tRT<*eyZ+4Nm=@f2>p0EMnNO`YiM4*NH>zyG4M^ ziX5SXcWQ!eH*lb-dYII()3}LjB}F6s*3Nb&$X%3jL0a-rQIk*WjVJQZBg=Jz0L?&5 zY&Nc=M}=!iothecemvLf+7h_l+saElskCcRimm1^W&B%|srFF%c0x@}^3EYNXT$oZ z{K$H{b55kbrd^(4D)Qp)c`0nmkeoGksY95&_OhvR{hLW=8-pF`yorMJ#EYjnRzdTa zLCOlZILQt3aJ~R$0bbXdC%Zd4=&2dzX{0jqR6%u!bDGtW`ethprcr#W29y<)P>TpQ z7^FUyA3Z-ng;Yn(!J#jhWdKu7_>DHeCl^YH$#KvAty7pvd~nhFjyTZ1l?yC$x=e0*i#6Bmnv@xd42Km%-RU^OIU zZ*@G-Cwp7|>_Mp#eiAw6nU(PL4$b)xZyQwArD(`CYhIZ={5vb)a5B|H6`5xRl9@7UD2ESVrXcuz_j6<^-NM+Z$1Uwo?d33*AP)w zUN_&>>C}%f4E~XrR$`jIS>b6Ic`r!wK zmt~NIEEqn#!oK$1J{|8Tr?ggL2E~&C;Vb9JQCU`}=KhW>F9Za7;Ig^#QapD*sjsAD z`$I^8_t!@c&kHYeInAAV3`IX~#iww7kBbR23QYV2Sgw4*d|Z=NjvYCO|FFmU;d^?C|y9nlA#W^pAl0 zSF*;7aXY8cwx3o3J5$~F?McXseSR%M(KFo_B|)hvm*OhsBR(NKQ^t?m(otSp$Dc=#PR%(ylqgv55C4nRf6S)??O`vPgmXS=M$m;qPcQ?Q-_3|r zkKH$ef6l16zBY8sN2&B$-`~2WL&06>Mph|gbri3i_^Rd5Oc6)QblXB>9(N74mo!7& zS$<V+A%3_DDJMJ6E^ zko;u}zs*bK3WStZ6`PP7%ptZZ0MT(oGv&mmWLxQS>vsY&&4=uZ5{nc58yg;b6wUb z_e;}A3Dj@Opvl~=SEAL)NKZMdmP-K_?DzFJ>v7js*vxo!*@#FiN6u*~H?8sB*t%=& z>cn)}DrX#Uq9g*jaKw+qgT$qv_DwNo^DBc^H-azjQaG*7`L1chZn}@Q@pR?}LYK3N z$WV$f2iwK%l*#Uw=3IS8(nqaERn#E(0}_fW%QJav5R7raf$F9aB2M6&N#!%|moY{b z!Occ8*|(RJx0{9`nX7)l|(107-W*G3M?_yXbb)b zEbsFgZ9f;Pc_H{DjTc?Mxx>g?k2^my+iY09zuCC%gkCM#okYaV*xjs9=n$FncWi_u z>7Q9wkxMAhuC8u#;oLxx2i1r#zT?b%!-^^vndzQXr<|gE8mPWVv{0FhL86 zpK}V^PA_p_GaYlXau_2Ae>Q^c_!^tZb~U!)KAAZMg1;Vc>e*AIY1TW#PgH=FS?TfP zU9Ga!2AV73Gbivq5sx;NQf>v2ka3%o&1yb?c%YL(HW!}Ta{XjpGkeDP5$uZ*X{cvJ zEMqB*?}&hBHU9Ires<{{d2&+!aqDp7X>n+Hak}-~>)$6?M&v0PN5zq{jJtpS)+b?_%5b*@g0XehN_8 zTC(xl;fC*zXA>>nEFWe&GB;Zr&B41x5}P}v(A!}qk0)B9!UZr2JqXRV-+z$|O_u^W8 zyDs`cJ>=jz;STs>se)dZ&L6Ib5N?e8wS)HNws|Ig*>6x0A1^May?aHwqV+#2ciNo_ zqEYilctePjMpfH0uX@Q=$B)GG&s$CzW@D^4^(E$lS5ux$CYOoIr`=0mPhS6i-SNJD zkxPV`Qo=?89Ut5l4jv_Z=V>*!(TN%Q~#!M zEfgMCtd8q~^`^Uyc`Odiie z-VUHpG%o8$Bf7OEGm7eG@$*%7lj|Ikvwvg4_uqp%&0ACQSY%0`U%o%9kWfTi%0^XZ zEjuV775m%sY}I}QD$mT!{4KODEoVxY>75+?qY!9hg!d6xkH4~Uw7an(Qfm-*Tx6#K zd14o@ihI^-31?vl3VJnN^Q(1(`<;)p%smaZ5EnalC~oV19IIQ}oC#kNcg#?qoaXTD z$qyKMKNQL*ATil(SuK_aZmp#t5`B|zGa=BZhO<(Nn#{uD6J=bsOm>ecTajoX^NWfK zFQssMeY85%|7*{qwT%Mi(bAF^BgfMt$M8hso;M6YMrf=C5i>v4=;!Mqzh?H-Cbwwr0@9=v_P zC?$kyR_fnpJCLQnkBgBxkGI4w%BX@``J$!^_(1|uz6Gq)cMMXQ^i$`OSi!1Oy zeY+Oq@6olsjAV>gR@Lxj@pUWW&d+;%R3JfC!z&EL*Oe^_IY&W?9>`<#AD0_lkYH$9 zuMz{XB`@1;0D~pIDooP!>5dIsjgX zh?)StrLKsk51ubTwdEVD;Kkkc)GaS6`Uc@SC%jX9+mka_0u&m{ zbSqSD=!4g9p?L6{+!&NDV#Djz58XPbW1nJy=fvy0!P zeKLVK;(LcT6dv-|lkxa)oIlNG(>hZery!Jz3O4#uzQ2%9T56u`dLFMq4BJ?Hp({@C z?(G2kM8c>(kjMJLsX`WhrQ`f;9bTcjHy@7c$SrQaeTiP-60rv9@#f>@sY{{aOE9V_^&Bt!-2^-*s2aK8kS24M`z+q=tyh0u zM>4!&j=q&#N@ycX`*-DQ=yFmfeSk^y^syMtQHX5Kko_4nynUrFE#K~fNRNr&28V?1 zh907gyhys+o7^F1LYMt;V`*R0<_$alehD}4ZG4fqWxPreV|+IiS6`#Aedx9%>QAUTUK&p{-RDKKL>knPF-Ls!lzUa<~b}AWjy~f7i=Xn za#%#jJ*j0FRXYw~+CLpFh~iQn^fZc%T`9G@PzC!I-v4eR*Ym(PWxc|$t4i&Q4$hVW zBy^pl;_`t(NSrs>31?@ox7dl^$Un;3T_g53@3$kB&0+V7=DbkxoctQNveh930C~)LeS6533*BYiV zY1(<^y-9kZA+La{*r#>ysc#TW)0SUTH-6;?^s|Gl|2m|z&-KjboaMHEjFq}X9E(s| zdoHt>d210N&1gcC0p~+Wf zMAb$~Jl{8{~pPy@aPeg1L# zkA&|!-)0X54+@d6SL^X|OlwQTPP)?ixbyt0-O@bUeA)H7ujE_V^D`xAcA4nI57ZY2 za}@6Hs4FtSV#;KW;(y6QCcp6TMoeB>K3z!`TM8X`F{z$%6&BB!tH)Ezux>3}A<-6J znsZK_;%haO@StDQRk+K^zNQ$2%!B9Jsp;ycJ+Ms9Z|uDJlc68QxaO=~`>rFGn?3LW zCawlyHGrZ+-2tgJbfm_fNBmeN?Utzz`zD^KtBgFC-{A+`fA1B;c9PP%A&{~!T;8K= z5k4vIH6|(^5~v=>@lfbu;?iBf?dQ&MJ!^v|Y!O)dE9^xvTn%_blXR|o7(YIXy&>a| zZedDEzhtDuw*~rY^;oLc8BJ9_D|BsD4NH*6TAE?;+Hk`O5gz+A++b?g=ZZ%AB?X~g z8qPa<7le7K_(N7cnM~?|aCEry>xE9YUd6&!tf1=y?x;Kj-k>Y>!*J@*1> zI0VfTI9z%F7jn|m(Tc3vqG=`4Db{+Y1WiiuT#Yu0u6uTE6y+euLJ?`U& zT@aL|?)-|scUynrHiupV)5|SGiA0+PbUiopW`f0HuBZSjhaPXua5>2}DThN8eJ33| z_8deXG!=#67j7Ms8E6*oBrKk<=uK%t{8QZCbqp&l6((Zu_weeICyk?*3b@Q}&8OF` z{Ffh*3shjCzp{eL8n$JT91QBY3Tw!CJ8^ENr@1WB-E?t)--+-vsGLM+&&>6 zCCLC9rmA?S1@+phXB~LYmI}8P>iYYTTuQCAJL14Ir-@zZv`M=8fVaUnk|WMZt7L&V zn7RpUR8L`vL+=c}Oa7-`%ss7TntY;>ZvW=md=<2EhpZ(Ivi6?~gt{Qq;%+M4WdB0r zz^E<0c&%ql&%uk4YxR*Q;J{P@pm}Umu2SPUWFpLVbNQ!hu|#rQu)qHerXe zFrGk%FHsf}clnf!cdOzOlk(~I&kI;VS))~Xe($(I(uK|39~_1SzwMS}*v|zYDr|BY zvSR0B{a<5^z)jl?YZJU3K7Ny&ZI3V=JL~l5g}R!h{i?+CSY8OaM?!AEo&2XohI~^} zU2!ewgBl3drTGT}cNA4&YR4&`I4aXcTo8!gJdX!oRSO(1@TiTmI4QOw+78P9Su#va zIe86s(KT`1mlrl^YbJYM3-Rh%#O=Ljp2R)Hg`AM&MqU_@6_Zlt9oX_QtiaRK%?pF- zbRfH>sN9^+0pPS8N)Lh6>0cHdkT_npc=c6hHATR-p+i4i30$Gw)OcXuMWle5_+u#XLbv8C%50#dW9wqSMxXE>d}4I8K9o zz_gdgvWm`PbQ_Q+Zz6j7k(Dt~BHPKu>3r1Lpll_H$-#@%9KrzMGeO?9_YZV8@+^MZ z{q*Y2ptB&A9I9ueS)4uuFa#wtz-jB`G|FY}%-J5;ecQ4Z)P6%(+f#8xyt&ggPDNp~ zP6;Q5bx>vhGxOv}J^1(5fW_49w~bdLNk(PM3d`HYwJ3zf*LszS_HAS{HFnH2KLzmH(h8P3@o z*iEYg7DAnedrccD%?LJ2<|XvBp~&YsyVGGFt@#2`pJ#W8Iiu;kehKeZv=ieVAv+{O zjz!&6Cf=m-8>&b&hvaOvQ=^T^v=^yJ$t!&ks)?7@qif9&2W z%SUS~c#`?Fe0yYJ+-R5x?DSXtr}R>8#{iCWf#7&UNub@4lVSd4umd>miT!HyB z=#A9e!*syeVx?a2!u`U3{!t;S)a-dFuAU#3?dy0jsif?ZUvB=c6EnSQph)EYK|BeO^dqTfBWO-#B9}X^uDu~)Ra__g(i9|xg%-H z@k-+tHWG^m8MUq5}P)KVvC?APemU)(w=h_)d8!r2R-<`7uH zM@}uiv0|+|NjSnphzM=qXI8;cjYY#tF;_a#dY^c@X%ALPMIjrf`n7_fU9=+4R#o4w zXvLDhc9xYw>SVm$j7MalzDiU0@jdN`~_-nu00w=Q7n5WmSU3KbJl5i_kBJ@i7 z1*3xU%ND~{f4~)>e?~Q?=P_L>>?LE>MhS~SiMdmD<2(Yvr=U%zy^F^Af$lH;xGkNv+pQTQp11Vjk zo`|9aT7vy}-q1Ar53sROExe^3(BRf#$Ct((&l9*O={97)EHHE_P|=eex?g&KiEt>E z7ViXa-AA3>sV5VCLtM#wU7JtN7!Sp-!0bhLDm;@9alol~i;~v{E-5PC^d8bb5}Se~MZWB&-#SD;p0rKV(6&GKXGOezz0mq3g>7 zJGB9m+R1krMYof5mB(VWV^7Gzi<_V0&Mz+(eC;L)`*w4jw6(R)S_Sp@K=7Kv7u(Ec z+JqQR{VQ7dYYt`fN#U-JIV9pNS@UC0hQ_S!)#=9`(7uZWkaRg0Lih~|J9qKgv3MKE z^YAJcwA?>2$$6>s^g`%02f<5sC)(rrbKx~M!r_f>>RZ=j#>(MUI)d2BoC#{QK}}=i za&FZc<$(kEgOMzIx~8hOKo}+V7<=@jTwqR`O*KwcS5>&u<9wqr{>GBo7!Ce_fg(-q z+5!y{Ve>RbDyD*+KR#-SK#W;_%c_`!UGQDjiK*@i zWL48{vm_EXWO8{`(o?I|^6VY6VCex{*%^W?)`bYN1qTex|Dx%KEte*Sh;Fs#oC_RX zTo=LkQMUSpYPt8-Kv;aaHE;#JORlUDV-<$XEw%ZwuB3h2w^rJiOw008(rp#PDUNL_ z3)6>my-a+(X8#Jq=j$8KI&2ie;-!|YGW;vNTfkE85vu+6Fm;R$@V7?0rJr>8hUbN4 zPw19iVAc^|Nld=XyEuhg0^RYvTHgBHjsw}i%R8Lo1t22<7@@pY4F}C>o^|Y7SM02n3X&~U&nNCeKB#OvOcP>fKu3`*40)7P`L9BwRM!|Ha`Pk%ur_*XA-^ z2hrm26d0pkJ^(}~oE4$4Y7l{1hTwCZ1ZEC(z9iVvDBC}jKb~sn}w_I-u1VC0Flz0HF?0hq;A$^Ick6mfYridf#)7^2W!DBP1u_d)bU?9jRAE{oP{N6=gbA8xNjMum;7~ z^-TzpmuPmcz$U|0Rd%-uw{pq@zU>5qs%Oso36Py01U=zMv*M?2Q29ukyUuDmmp$n5 z^?~aRM~A$>UuvM2Lfo_*yv}1bX&T6FJ|nAT+ic7cfd|{Sar5%Axvik)M`M3B3*GXR za6O=Ke=XI$RZA+g6lnha=-wUhK`41g*5Zco*|l>mlE#TJpjo(cHXFHz5t5+aBrR%b zmYvi_Fw?Y>t7+E~i@>v^hd<{&*@%W`)c`8*H}TDNCV+p-Y0CDGpbx6PG70J#+||6Z z6CItzv_UU|c`W}`Eo@^l5t6GtthUGp&XIQKcPh~2!J<4G_0tB{j-^!^u<=Q@qgKOO z#_5e+KEn>8&(RD2AkL}nM+FJn0uNo6c?jx&`TbvF1)iF{(zwVfyE!Rw(w1nwdSyb&>b?!w>r}2hxIwSJC785-p`PrUtuXGpi#0e4Hy&}+l?r(JnR&+er8PwH9mqlb2d z?bIaP#nBP+PfwG4NcU+JM55|JvjZQ)BnO!Fd_Ee`alLJBzs>$MjY>N^UxCRhUW)Qx zDJu}V;L6hIGXJu*MDrL%gGn`L(bHz|xt`mV7=BI^!@g;|@Rl$<;_ZzXP6CIUuqNh( z*Whoe2KTG?2$th?8-a?s`&Dd&JA_BSubp2X^5^tG6~=9}c&(j1O(D)GEB|?Kd;xwq z?!yH~&--Q$LvFb}ZteIukh$@Vt~gjx2w}3tiRWr6U%E&K^%qimM(D4aD@nq53)3@) z*9n#iL8O1#{aXCSndJYh?IiLDoEV(<9H`ctqKxBr1#gdz=+&)2;n!L}Ql1?-{v7k| z4yS@psDcES+apiq$2`1i9rPiLcnEYssV8YkDUpzTx=xzvsO_6joHuM&2T3ME$Gm)*uq75s?07sC0r zACpQnnlz^-*_uL68#MxKP7ep>+(4(91Ct>kb}($}q=RnoqLBMy*uqFo%aF?9K7gq- z*h<^E?aYIOZ6)+Iy;shb?9Sk9Sc%XR*rI`KyjQA7n==IH*L+W-7^{?u-7k3j0Uvb4 z;qZrS>Zow%oZn9G9#HBVRkF0cl3Fg)dY?WwydZ|tlK7(LS3l0-y$s88ecV=JPuwK> z8{$Iqk*Y&zjZm@7Swfnc6~f&=7i1;&Kz52pkv2GuO zLzX+GVq@9#^{%*#;}b=<3Y|iv%IvSr??S&|RzeES89C$#q_$c-D7bhjXRF64zCu;^ z?F6Wr=|*VLN~H2TZ4y6*?t&oxZ1V#%3q$UKFq)>*Ua!?qoI8i!c#k&Fx(%c-DM$8Y zGY9t&aTZWiiS7ntMQ{r7oRkypG2*O!bRYcS4A$pic^!?adG;kNVxx|%OAa{;nCg>! z4{uiHFB1KR@t!yx6-@Iu&1X|u?-F`kdwqV2CZZlNSZ+s2#-FU;X&_^+?vVzzUNFBN z|Kte_dED7^KS@CNU58)#DHn;igJA!z<>AL>Ah zSjhV=SMeu+48b*FDYn|QgQYO_gUNFIMm^s|j560&!0$42)A+J z)}vkU#gJo@{wc6~vNSJAE^2^*NoJ0;(YWXSeDUXrkV^a_n?7$M0nngVl&AFybP&Kb zC|6bPwn{0gUO1@dn!Yp@c*J-CSA?DOU6yzPjI#kM{oUvK1W#w9cZut;|1S+ z;*6G|Oyh{gXhU?GM)7UeFV|i`OJIUclm+^`sTmKyrO%R~Cg4!V12AUNfFFF`P(hVJ zXVzsrt{sO?TEVtRp3RqsIJ&pa>XOD9^}n`tOnB18FekdGxEg{~S&mphOD0MZ$B_f? zHuTkfm4d`GJXrHFNDb=+qT)6zEjATdrkt zsJL^=y7g&&WV=Rk0L73b^cC8hBs{G(0HNo-*1A5b@f`t0NCcAufP@h{R?hz`ehGp0 z=~pr1^NrnMSwLmBHBPK68|nwNfyPn0%0l*Hx%7Z`zN)QrB|Z6r93-QioVt!%ehk?j z*!|ys0F|vckCE=5efGvukmswiAOFz%OTNXnKsol*M)`p6{{*ez1@aK!vwEomXo3(M zr#;`MK59dzc7Z&0RC(-RjBL_IqA!y!*)4TR^^h}0C`Wx|KR=G_17-Dv^p_&janz5b z&lD)f-{~e%j(t!YNFHR#>V!Nd*o*oK=_!|uJQ}|z8BVsIJ?;W)IsIyT=IGx0tL(d< z`QVLqYZy2|80Ikp3F8DuM#Y0o|Gt1^FqxpT)3yX>52Z0^}l*fBM`?a5^2e%JlS z*t(W!!IqNxf%?aDfxK?M4dOoW{A5$-UBq_s`hxa+oBF6t_G6F_=>E@&vPm0hulqj- za>jtTuk7c?kq%H+UyweiA1S{7k;iS6V;}fDAZZ9Icgdws9)m3IE2O7fHuA{kC6^Q6 z(>-|n498nrG!S>De6T02Hu%jaot)^zR9};(CY!Wl2jX%1YflSVmiZtwR!CW$cJ-so zu>g$&y~@ddu^q}mo|VZpu6&H!R3>R370?gw4l-Nn8{1TW`3uxb7egO%_F0j~ZFxQE zBIlh={`g5BJ)j3_I;T4SB-v8m*apOQVttT4s2}N|-~X9A z$l|_2ddg)Zk8IM8omdu^^X)P6L1--5P+#?<%&`FJK)xK?p`4d%T=^KcsZ7e>|4naO zSlWYE-|&~LBq#~9PLu@~3w6oVFTUx3vh0Z1uEwH_Tw|#Y(vNKjjmM8cv0_R;3@zX2 z*pW?iH6FI&vY;GapsB6${AH6omZ=Wv3*>Pb=pXtetJ@e|3>rgy=Jk>$G)c5R$5n%9m)b3NG4kZ`M#iCeSk55{z|c*z?dYCCCCp@j=t_sT^HoK zu9ei6c2GUGgDh?rvM-tXX^bJi|C3yOut`orCY!o00b6ldP?k;UP*$64=4Gk_W!WT; z%Rqnii(y->~ z7Y=9~=?Zk{qcW>_ZYcC52W--xi%$OPg3!r~k>9jyOs%ZcratMoY0@|vo3h$^`*>Jbz@enO|*#?8q*tKS@4tU7`cXDNCl{ zuj@-&fkL)rQwbQKG17jiebsfWNyt=}9%b2$WwE^Mf7u5?y+0YppfBIZ(U(0?jxH&_ zc7(=}u0V%ADwF7sp5&l&@|)fN1sT3 z&auV-a@vHhXXF|;KbCUYjOFO|Wk0@8G$*|vsE_0%NOcK<>5YuO$}!l8`-8^UHJ$g7 zBpFDKKF}W-b#m1iL+udHOJk!G>&b@nkjYo+(WYE&^o#A%ru#3pM;_aRxKCad+lXa4 zR)8Fv$|X}@ppFfdMJ$VT@UO4=l43p3n8@f8>ua8rW4Xxd$Pcwip4#2}{tq6?00{*; zC{gETl_3raGWDSx_XX8s8BoRsa@BQ^)dsZdLZYm(0Gae4zDYD5i9RYb26cfRDV`T_ z(}fE${H{xRUO-NpkZrYNLv`6xo5oh2ZwK`UgU*rsM@~6DcRDs2i}rYY*#gP)AkX_KJLI%cmM!_CJO=ytq&cO2l=bA3>|>iU z`l)W}UwrK#(_G1=BY%W!D_6S~4g`7@CbZ*|^ng5`XR1>kN5@&j=K#>voM@8`keoXD z>LWQx^HQQdvhV-0eT_$=kIL~m1oTLI-~Z+7B~CydAA8wWJ2q68J+*0U<@t6{e?T7l zFS$^Ee*Dj7)^sKydOKibd6 zKF|j=9(t7NuaiSL_Dy3*kKZ1VZQ9U5E?shwuTxf1AE1mc`Z<9-(g)3jy2|A8Pr53T zbUaj#+kkdZ8z~+q2Az1`8aMc#*%2I5WaywTo57!GEY*d|(jk`)N#{gKP}Uf-0m$WB zJT|%{u3zff&XXQx^^-m28aM4%zOkv}pKQxc9`(mwY?I)QA6h`LK#|WtE_;j<&qub@ zj!X$;LvxDf#vEchOW-ne{u~=MvAY480ib_gJc>HJ<9YS@clm~{oQ|{ zjWTlSstxjW%1Y`3l<`H!GmrE^bD^#>x%`u^%B0fq%TnA{`ng~XkaHkV0P3I{S#sSl z5F4Y69qCF3)FxSMJMIsZ(WQ-ph&`PM!Jm z@_9TKW%Y~u$bad^{gh&KK|*{yWt$WqPw4DSw?!bft#eC~o%pyz>~m}bkaN!a>raAU zBe_X6SH>lQY^guUuUHRc8?Y%o?CQ86$BxEVirX}f>`_;L>Bjv8da(`075IV;+Ev#$ zvCTB^eN*Z~RiHnsc#>?$4)xeB<536MihZUW>r0n0%44V3Gv|>!uS1=jv1CVMXugsk zvim>VcBPCiZ5(^->6#MAK;y=_#-}V)j%9$J?9vwdgB|I|I{7>vi?aH~edND%<9+m_juZd@002ovPDHLkV1l^m^~3-G literal 0 HcmV?d00001