From c9265ea20c1ef94793ac872645d8aeea421de23d Mon Sep 17 00:00:00 2001 From: Derek Christ Date: Sat, 3 Sep 2022 14:29:42 +0200 Subject: [PATCH] Pointer events, keyboard events, frame callbacks --- build.sh | 2 +- common/state.h | 61 +++++++++++ compositor | Bin 43280 -> 76664 bytes compositor.c | 113 ++++++++++++++----- keyboard.c | 83 ++++++++++++++ keyboard.h | 15 +++ pointer.c | 151 ++++++++++++++++++++++++++ pointer.h | 19 ++++ protocols/xdg-shell-client-protocol.h | 2 +- protocols/xdg-shell-protocol.c | 2 +- 10 files changed, 418 insertions(+), 30 deletions(-) create mode 100644 common/state.h create mode 100644 keyboard.c create mode 100644 keyboard.h create mode 100644 pointer.c create mode 100644 pointer.h diff --git a/build.sh b/build.sh index e67bf69..bf37923 100755 --- a/build.sh +++ b/build.sh @@ -1 +1 @@ -gcc compositor.c common/shm.c protocols/xdg-shell-protocol.c -o compositor -lwayland-client -lrt -g +gcc compositor.c pointer.c keyboard.c common/shm.c protocols/xdg-shell-protocol.c -o compositor -lwayland-client -lrt -lxkbcommon -g diff --git a/common/state.h b/common/state.h new file mode 100644 index 0000000..4ac3f62 --- /dev/null +++ b/common/state.h @@ -0,0 +1,61 @@ +#pragma once + +#include +#include +#include +#include + +enum pointer_event_mask +{ + POINTER_EVENT_ENTER = 1 << 0, + POINTER_EVENT_LEAVE = 1 << 1, + POINTER_EVENT_MOTION = 1 << 2, + POINTER_EVENT_BUTTON = 1 << 3, + POINTER_EVENT_AXIS = 1 << 4, + POINTER_EVENT_AXIS_SOURCE = 1 << 5, + POINTER_EVENT_AXIS_STOP = 1 << 6, + POINTER_EVENT_AXIS_DISCRETE = 1 << 7, +}; + +struct pointer_event +{ + uint32_t event_mask; + wl_fixed_t surface_x, surface_y; + uint32_t button, state; + uint32_t time; + uint32_t serial; + struct + { + bool valid; + wl_fixed_t value; + int32_t discrete; + } axes[2]; + uint32_t axis_source; +}; + +struct client_state +{ + /* Globals */ + struct wl_display *wl_display; + struct wl_registry *wl_registry; + struct wl_shm *wl_shm; + struct wl_compositor *wl_compositor; + struct xdg_wm_base *xdg_wm_base; + struct wl_seat *wl_seat; + + /* Objects */ + struct wl_surface *wl_surface; + struct xdg_surface *xdg_surface; + struct xdg_toplevel *xdg_toplevel; + struct wl_keyboard *wl_keyboard; + struct wl_pointer *wl_pointer; + struct wl_touch *wl_touch; + + /* State */ + float offset; + uint32_t last_frame; + struct pointer_event pointer_event; + struct xkb_state *xkb_state; + struct xkb_context *xkb_context; + struct xkb_keymap *xkb_keymap; +}; diff --git a/compositor b/compositor index a5700eea778f7da9aa2748330f42d401772fe21c..29e109e200a31bf5abe0cf2cebb62d13d17eb136 100755 GIT binary patch literal 76664 zcmeFa33yf2xj($ebM{G2l9K^Q7!PwmhA%y^QTKyIh|4mk1O74tNy>EuSAt=u9kBA>4&PJb*lAUj&k(N|MpPL zZmd@N{QlSQ{!}SZr#jB2Hq>vJJ#%V9_2h>7#`ccM9fh+e&z?D@wP}jHFlZM;i7|EY zrE3kV(>O$&E^iv9fJ4CXuoSH6jy@8NB?Y>9onF8%9Ql+C>~b2E8Z z50)Vh590W*50)Q?fAY{jmuHUrbeCb>McjD&Pr?7MzCL!JxBrP_9ouesE^ztuVIQ4z z7949V+`s>tJ7>;$uV?6q8^_(}*gyPIvG*Bm9!m8+9!1<`^+gFN3kIAEPeIXh@OShh zXFYh&C8q#_&V~P1Klt1Fkw3a0JD2sN_ZR)hf4Cp{2m9gwLO=X}=?7m1Ihp71+v$Gz zKk5g6Pe1kgb3b;b_T!(ze&qk8AN(-rjh@5){C@ba=?7ojkDU+p!+&c(^}Vzoey5-M zHub|lsh@gn?Z?iy`;n8`5B`_Hk2;6nUI8xC7+`dMN)q!G&jZj){2?E{N%0#u_2qwB z;X8f!Of!5Z(=yIR zgw_a3TmurF@UYEZ*6X)V=qQd{dbL0wZrU+xX#+n<-Yg?K&p`}{t8#fvx zq0!oE$WTs8(WK?AH*QLzpp1aP0fJy^tlU(S#G%W!x78L3G#at3rlXDOlGxgttEp*I zW$Sgx*v6KcS^+B?+XaHvZ)&b-G^!e!;8&Q|Ru84+G#7AH-F4-)mGuokRJOL(AnF9P zsl5@^G-}&g+8e8&3}j3rs#IWZs;qA`TH6rAEk=T*HPqKMwsFc*Ks20<_``J)$5b&l+gMj#Fl82aE?&8O@sjfCQ)b3<(=Lo>r%#y` z&(E02Yuv)z#l&0$Gabx=0s?Yz=07R&tTE(q1(rFR^CA3MBF|jnk7fBM59Su4ujV`` zivObA(P7TIJ^>sLT5!3d6V9ox$Ba5)R4LpScHWI?kz;I9aQonTOv!A6^IXb&M9?kc zR=_;;yioUFkr@(Ss^;+;e`vbISNQPDE|U0WAN~(>CBDOlPn$3Coj&{sg}>d0pQG?Q zeE2$r-|NGtFOc&0`S8C`{QG_QhJ}*L=sIv+k&;hTN<=%*!rhYufC{GC4haD~6!hc8lccKGm7 z#lPE!FHrcsK735c+2_O0Q2hIS_(Fw0;KS>B9rEGlD*huryq0s!hu3mW`0xvroKrr0 zg@TRirC)XYXgi`lyl$_2A6~atfe)|kDfHo2D0_;0_%#Y2^Wn9e5+D9b#b4^fZ&dgS zA70C;^Whs5f3pv-{oLWhYdM`h{0&OZ?LNGgv%`nia(4UhT}sYgA70DZ=fi6``+fL( zm7D`Uy!P`UA70Bj;=}J#a*p}%-&6SGKD?H5!iWF9;_vn04=emBA70Bj?ZdyI_>CL- z#_b7(_k4IQC+fq$q4*1Y_^8rX;lu0ruk+z`{5SjXT5pFB|DMv@>BGnSD<8g8_g6mr z86{`84wpjcX~loYhrdnXkNEI=m7HTfe7E91?!)g;_!B<-ekG^ZhyT3dKjp*g{`0gCul-}( z*f$RES8_ZbUdxI2@LEo;5C3f?C+frNIL!CqwVVPU{s|?g(1+J@ihOu2C+5SysN|IR z@H(DLefT5F&lNuWua%rSA70zj?89q&I(+zdm7Go=Udy@Nhu3oU`tZ8{+2_N5pycoO z;kBFtKKw_D|Bw%_?K$GZYdOb!c&A#%({UeufWn{f;kBGzA3jgceaQ7Wwe;e$|K9{weX{wLPUi{2XOZg%7X$jXEFxn2N(@AO0dG zr^AO|uJD~c{0SvzuMZ!OA0J+ir~7?)U9ST^{3@mQkPol@bHs<&a*p}%rAp3mA70Ow zPWbRzj-mE3^}3)&$%**zMXG$%hmZS3`AN(16kO!PN7cBj*HxO|P;sT_L2dr$i4Ou`pvB+|Y8;Pv=JIm!GwPW||OH%pCgF3E@V6)72PENlB;f}p;ddwD2PNV6CgJmv@cWYR zgOl+4lkh{5@CTCcLzD1_lJLWl@JEvH!;|pGlJFyv@W+$z(IotdB>bo(d~XtdbQ1nl z5`Ih){(Sbl2hMxoya&#E;JgRUd*HkW&U@gz2hMxoya)cj?t%9+hJO(2de@8fxDQ=s z7_n`K+N?9jVqMRA&j{1c%=&Mj&y0N$=Zq0W$Pun1?c|9wIL1CrnA;yGj|uo7VJ`Jg z9un|>5$4v*$^8QU24RQrUIBlFFqir#cL?~4gt^2&*(u=95#}=fWV3+pAj~EG$qE7A zLYPbWlO+P)OqfgflSKkqs#%q^>v6#}*hbLoAuM8F?h3YbgolSKl4k1&_oCkq7p2f|!p zpNtClb;4X)pNt6j6~bInpELk7KBoO~dG}ARiFLmo>w5E4$(rJ6ho&8kZC|iD1A#H1 z3J<>gwRTFzh+9zv0knO=JuoP?-TfSsIj7qOAhec&iJ=ubb0T9zC;6UHDfsIJ|17pc zY{Eye?o+X+{)z5E z>nZMybsz4X`mZx*x}PSr=rLAe(E*;je;V69;1N7u^aEZ6;1k&1r@h@V%Icl(>*J>I?Q1lW$pdJ2~I6u;R+Xe}Vv zg^TxEqi4&BE7uiwKP4^t0nLgVGKYqAJ&T(B9~#s7VUjTs7$fRJGarS>9dSGR_$U>H z`ti}RW$;lS)4mMmv&_=f?p}MMd)2E;yNi!6?Oyegs4G-|^M4@!DP8lPwJ)uPBk=z! z{!ifl&DF_mgO<4}=GU4}tzFMTkIx}}>bRJS*x08R9bm(D|MVbj@7u_~0aIT`d|c~J z1$SSsp>WSE}ApW7jp)#)obegS@)0?&N` zw$e2mX5w+bh_d`{=~L5kEe=i0+UX|d=o1(70hs!lbGp0zuia};b+39C!5Ob?mZ)q` z`(H)vaaHR2_N+a%dQsmxbGV8txCaG!efpGqOfT{K(@AZ|hK@UEJ1G4)C=0!2->WZ5 z#eN6%5qDAl;<}djuZ5q2`JCcDE)0hzSEWzmJpGSzhhd*i_G%RW-}~)55P9yHU)zr| zMn$}#PJyYfGK>oC@vApe%Mg9?nf4RO9g!F$?z#v*w_eFL^n+%G@N%#Uk;FF|nJCw&@!kx&T(&K~WaU+8cAm%w~hA`ck-yeN^Jg1MS0&%&Z*}B)h8tYzljN=RID2*S` zun0!<6d#LqzYJS?hoS7YL+$xJ?XLoR;xS?4_5sVt1@aXP`JT1#@8aB(kJJ910oV4G z@M~BK31hVc5x(08EL9RR6A~it+=SvQ=JqjQkFU6B>Foo?k_!g>jPWB4h`zG`#UJjg z{c2f!wARjfG{(f+l=lBf6wlY*R>KB*@*P*pX$$)o5+l&9+x#jOhTQO8)N>o&W zscn0q0`h4`C5Il_EG2O!!rRkUT&Oko{t8_0%*=SS`0rh(%v%q&O^CB%sys9fK;ElD z9+Vv}DnSbx!Wc^#^N-Fg|40}UJIk2S31ebPC{kg}@PsjsX=DD;*BHu+8xv#Yo=6yj zsg+^q!0CFo2-ntegvzn*wJ$=`F>*hN2t*!eHdCUhhB5HF!GqyZ@NQ2#HY@I$t7NHD zJw??}zm5fw?OFTcgr^ah1mDky6jjt_{Q2nn8CM-adg#53iN)`}mk}9u`AEa4&B#CY zUdA;)2kb5f{9Z=c)9+=JKJ{M4RflBh%%TcrXkr&C@$qpsKwKx%_(cb>I=J~Up5z3E zKA@Oal9&kJpH`vsQcJsKpF?-K=J$!X>;$6 zQ=$n@#JaYeIMbekMuOGv04c>ZqXb-Z01+O+JiBMrak)mB&$hwDr>Ff`&#D)D)*b;9 zh6u`E)O|`%F?y%ql2VP1?&EOpZopE-?bHjwq6y#6m~^cB*i)xRJk>kQe7fuPAX`}U zA8eAt#fNC>$p>&cT>Pxa-;Q(7+GlYMY!6#KYY!n~9@}0L#N`ls8q3C~PT1RXOeJ$D zWoAI;v98m>o5%MQAA$pqU?eSm);tCeZqM09p)luk%sh73sI9F%^Um0R%l4-~{pm~> zL_o~E;v*S%HXt0jiVp!kG_Ux@j5|vJvx%w@-^C}?U{WCKeM)v;+tUj0+ak5@yO_>G zIH-~M)9;>uQQc2Yf|ywM_qb6}9qVz%=F?QTW!jcL!qKzkEOTP1&$F z{E5Z3kwGGE`!f-os|qBWW9VN5PmCIRwuwZFL?3k}e%z!CTS~S>sVx-Lv#pM$;_Rgf z_IcvE-BwCEl>AyMX_iXpltd{jQR>O}D0P=&s}P07@VgUN1}f4a%Zntg^=H%KMsnr_ zs{|cdrxNTg?u0`-G1wJgb3?VjwS^d(Afj9nz3L-%pMjO7Dphgt%~Zpj+otI)z)27Cs1o&?cUMJ$RHg% z2qg_Nhu)}J58z2dJ0ydikl3d<#anUlXP53?g+)5zJy`(DIPo%4_p99{nB?@e=cRMn zqa>oDea)g&&nFdiQ&Rg>8G_rSvSZyP9shfyBR=uc=v8!LtovBZJj8}Wha5AH#N1oq z;pYt#Q_*wMx9Tfnu~X%2D@Wjhw9MO zdy(JUQ<4+wahJ}AgzahVgDEI6kJrXFphVZ{$nDAxVdK^F{iV`RG<6y*;0 zwj}X8;7>B;kJy0mnevK4(UdyBDF?B=8S74%^7TI?YUx2VCt`asO5rLD!SXMH{hgr7 zz-{{kxg-pO(`azds(rETv$hGLdj1EAhl}q##dg{5b_i1Oeq^tg*#n6zx?cB_5_<&M zO)`5dkv%Td8KaBu#E5k3%^XD!7w>>%cu@}fP2hjW+!5<4-eF==v%}1|?X%d!MyUa!)bl@N zsoq>A_Fy4p;2#1H_rCsn(XAKvqWhL3ExPZkfsFNBtv7I!x=Vjg7wtZ)^W6i<=j?S* z|33Y!>b=;t6(xx|caNfboPRI5s4m>;6Ne@5$97<6)3@)q6V>3vO?O~=`YQMOv>AP8 z|J|#OU_5vh73IYLMAw!V&9;0**%nMy*m}JG0r0c9&%vPE?tWh64)?Bnn-iyJF{@0> zT-p8b{VHUY^O=|5LbGr_)3v4dOgjaSQG&%(XuCTrF1S_+exF4+7whTVM^QcPy^kT) zf)DpTEoEXkO0&^EVg_=Pu;&Qk<7ZO9DaOj*B`=3w3fwPj!^8v33bj|E*2hs;=5+56 zoT3mn4R(-AFAU_I52Hd%%zwVe*<1In`2yHpfMjL&wtOVXBLyddYVy`CCuKeE#MyMz zQwK*uw!T2N;{FoteofRth~cb0*1e6Bt&>M-Y>&wI7~4xccSUmzb*vYcpKZfv;;J~H!4XomNwo67irQ1Ghq||=^yhiUy!Kbw~`?O(#Na>GB zxrckN6DGTBAXSmj!@Pm`Ngnp@rpPWK(t+`GQ>>>1U6RTg9I@vJdd4kTprT(b<~%Ka zp|`!E=V|Ju51Oa_6xW*K&WrofOlo}UUllI$|Y?dCu*>%Y4mh)-ZK&nxCc9nufHcjvA|E_6n14q}B3?fsEkUzxzNHIb_ zfefNRwzm6cEJ%93m(<3Xl#_&I9R|$Zf>#ZgW8g0pGRh*=Iz8W`Ve#gBSn#>KAUV-| zV}zu{0P#zhcUFtM{=1|WDL|)#OH%CG^)oZnj#+h#i`DkyyfN0ZC30o*E-bHstDr3T z7DHmc3wJL1_GU-??X#V0In@VQuupar#m5g9zl2sd-kAo(@vajY-5d*$iI|75G>t8a zF9nYv5WD~T_DJlhEyvxi;+M>>EibuSkDgWew@xHgKI${$aPf(12HAnBe9KJTrpf3E zPsR>RE)@sb6B`xVfkEi7!WW=Dx;C9Ki96Vde)I=Vix55B`&(3@yZFtnEpHkfnW6~T z{{SRzF}^bhy8_16aBm7s!F2L;X4_{EKoaWQcZD;zkEv{@-lbr}!lY}n4 zKsC_yl|p=eYgC#TR4C6wCSzU{IS&&CPfXt(ROZh{MgpoBt-OYQr>_BT@eR4+6~$;V_`1>HOL;r0HbR* zf^@^C@(pNEgiu>~E8k*UUoY~Nja6vF@)o{NxgJLnQe-!_R9;`-cB9CnQSn+^ydbv? zuV%isy|rx<{3Iw%wY7W?E``IqsVO!)@RfDrYG13I}nu^&8t;z}jxm{G>Xu zMu>rin(Gl$1e%+g+nbHXg-uOD><#se)lE0xmA_4!YT8#1B}*=HD;g=!jt9I2%RJf43#ZFFB|3%d)|lG zgT#}t#{L)1oGC!}v;RkD&g3F}=*MTylp;Niv;*nbpPV_f8|gNr2ax_3(&I>fg!DAh zp~p}TPc+?)v=HgbNK290KSg<@8<6fsdKc0INPmL#IMRtfLwTe(BF%LS<1wU#NQeI% z<&myH+JW?oNOvPWiSz){fiIyv(sf8rBke+(i(B8%AT2~X@)szN^jf4HNWXw|H`2F} z9zZ(vWt2y{8R==H_x=*)@vQlGkQO36inJ8zX`~%UYmTEl(k~-Dfb`{Gp*+$LUqN}K zLw=3&c%pOUZ%`iT-(N#{qy^Yd?Lc}5(%ndpAU%L|Fg8?=BgNBRXHFyi2x+c|`s4n^ ze5CIqU5|7XZn@lq^oL0IAbknxA*2IvGxP+~FZZ4~V}uOj*GQvC3vlzi22aiokn+GS6LFZER~u$W#2gY1dOHvg#Pdf-nI z0o?)mRya@>Y9X|9sHb;aVw5rhh6Se?92egWg~LZUVgv^z!8L)>U!&dq98sW6D1P`cFPa z|Er+?8T3!|kuM!E6u1vTpNMO_-}<9{%RnzmE}vAt`JmqjdQCFjs)*aa9`ql8-f#Uu zf8k^F?*aXTkD)&S`ULbn{mFk7^x2^IXa9$wultztL(wNyeN6fJpm%|OO`rOgCF&3Q zQ=l*JLth%F-vs&}K1Tl@(EkB?f9>-G=;P4G_9y>U&@TghMIZgk;`%=XeKY8z5eM<> z(;jY;f$=dEed-qU(OJpmSBc_+J|FZZ(D8s#qWszfeLd*)pifPvUz?!c1bPSPo0934 z>;R~L59rTwzcH!)pnDjLRwvUf*+4A+D(G`Tuj)g;MhGDNL(uV<@|jQfq2DCv zQQ(GROnVLV#}OxS{jo0F{x9LajHY=pr6Ko z{$0@5_9Ea(y3ML$-^0+c&BDZKvPivGTee($gTeu{qIzm}uF zhvLU;I~FQEn0kr#tm^NZaB9Irf7e7pXP^7Ce}$5(-`B3~(cd4@?{(IA{XG%Co%;JA zetegb@5k%!clh<{?{VnwZ)m;x`x=LpJdJ-r!TNpA8t?bO8;XzqZ+oRvR-5*_l4}$gMzy1z{jt~9)2QAN^ z_E*1UVseP@k*c&q)kA-$L4Qv{`(1x`!H?J9QPAH_=u~p_cM((lpt zm)Gyb*YCmC{Q5oj`n~oVuisnGwok{ueqTM7s^ZY^saN1xNq_CH-#@S0N58jQzfWGj zKmLUB(^-NQLf79P_xe5X{`%|py8pZDTI<8^v^ZFQ8~^&f?acGg?`xOP`SW=X{J-M? zQ>o2YfvMqzKA7J^mIDCueF9?nglFUnt< zpesA@t37e({_5PN<+mv;d{?~Kv*)Gm=6ujvlUS3vTI;00-c zUJw3QPms>Z|1WHzOMbq+k;gr{!1Eancsne=7gLE&wUtfHOUhP z(&M)0PL;CwTRZo2V73NlSAgPPO?+i#L2iw;?|YnH0Jd zA2;^giGXa!`HNt3zCtMCeqJQby-3`Lcp54yoH_y?jRd~m0aV}tXbU{%@*DR?z$0)l zWeWhya4tt8RBcB_s7f{kK+fdJx}UgwNOrzS98VToCODdWKO~MP>nL#x ziTgQmJXycNDfN%QMS_3JM-q4z)&&2dtojTv!P6PM6=MZ@Sp1(k_XBVO&k*=9dp@*! zhVy$sCQsI6l*r<(si8r~^zH=Yy+_u7DMfm}rTm~NWqKSfgPxhrMsmE5$Qd#RjG|QU zQPNT*E!S`^ge;RMYbQ>@wJh-^;&`$iB<>;NzC#>O)?>uQhF_~df0vqttP#2g zCn81AWf&orCq=+PBgBSc^QibwJp(b3;IA16fs>#GUk@D?284Lcuhg7DBj}8=qI4~Pz zZ&~WMkwgrq14(E*n0|navTfiS5anHLQ;-v~_XxAVvH<_=)mBom=+!15$6oE%__yYP z-5iuU4|z0r1ri*#?46vM1bF9`0zORvnY0FoOHeG)kzNlTvit|Aqt9Se8OB#REXBWn zXkt8tWltKGJ!x3>q+!{UhGkD0mc3^ddrW6SPBk=+kv63Z%M7qrO=CXmUKsDbJZK5>_Ps=~uMG^&E@hs5YH)B)Gm^0E z=fa-o=b(t4UpONAxk0-khLP*tKOQJYG}TwrCn0Y*M=-7T9u$D*v_%1YefZs=M2xie zLAQC?jv(uxt1=BE-#flDF5(p>;$^H9yjKIPrRSJX?#Z>Say=kTUe-=i!6a}$Lhu`mH9_dfAa>J|0#B8%sWKhZh&DaKS804C6FF5r;9Gp z%Vg1oPUtAehIbJDH?MF);(9RBJ_%&#hd7z6jwy04Fvlh^nKSHXDYqBXFPlA;DCy%i z>>q=YaT{G>e$`?3uc`k$dRxd*4Zjcr;^9ocz&Ma#+EP|X_)=$wSkTc8r2udMm zmNgS3$eCp=0|s(tS+}8?A!n9V4-(|evi^zcufBNNZ0nEc5Ee6&MqL+ApJ{cW3s}rd z1(cR9M&_p|wYn6Ual%w&4uW|RNo}xa^&({W*xzDiz6l4FuAWg~6#-Ma7?}cUFGZ#Y z%!`@%Coqf1bFHutnMw4(3?Wgru57JUh`<7(%sK|+fhe;M zf_YKd+I7}9Q2f$$@XsCS9WGrreVTPlVrE-6pca>|n?22{KymQSvBsmZ%GRx&W3>y+ zv}x9Sfk9>X|W%Pc)V;M&=E8Y!NZL*@DFUjwDtv z8}YM_N^gff>(;_QkHLp!)29_!FUrhxD;p|+nQeUsa>{0cw2?lXiRx^Ync3D(w$3^f zUx21WX13LgdX=u5S!gvuMOneh0;>W7OPRSvVy0WI5;NTzE<9FHXr)rcTBs-#0lBEq zvavBzx)_S=KG+tuANXdr`s5XBJGe?t;VN z=R)gs)_pNDpJntC^E3>Dp9_tQ4rukvi-S=_i}yIt=4HXqQUDlsc%6sl;w1Y9k$Zs@ zdm)f1jc7-EEOHscP;<}xaH{+bL45r)HB4>Y= zU<4i+@(a{m^fK>cJPKrX7_gabpcSp`CxLK`jBlZS5i=ul0-zTFU`~mI9K?&^aYQpO ziG+p&@{BY}2z`K)R4sDS{xd+Pyb5mnE%J~*iARixb_mFfAK_GDuF8Cp5`Q%WZ0j>a zrvPAx8wzMsW+;P|_m*Ls&iqtnXa=B&cLK8vb5~|)J)m5#owWbT40QsEdYe#f^R>*- zEVFf?e~+Dgyn!NYUSqd`?D6 zH0(<9&MKu#%Q^pzneH2S$kBmO~n=`-Y^9p~aP>~E4x!~EoU zV<@fvO-9=M@r;W5eR8u-N!+3e(wieev5Rp^;kN6bFCuh~ho&7v-uVlzlF*DGr-6oZ zHO0&halYy~<8g(DE)+n-`7wAyg#yTRW}=4*%@IJ&UJ{U(4`rd0Jz0jMWvT{ z@1tm!^Bk34o?)Zt?an$1C`-EwY&)E{(A|eVF(?hV-OhJ-#jVd91Yoaon!we23hujDX+sO+XFV`VMwsEkQ0oPG-I}5vuQ^V5%~=Kp1L!g z`5_;{1MIHDMOF>q>|W?FvS&hynf)|eh1KGP!fy4RQAZa^6Czv|;JY8_vDB zVA6)?ut#v5J?P`oMu<7Pht-c*Xbu_&XBMXAOh6b1ybhz%atE?Z#2GpQK%SuFICOm4U;*ShBVc1%G)Of=9d^rUBL!uq zF|-MMbAT6KGmSyNL0cb48&iO$a5%9~8=t!o^JrLvC`!xE<8<9~I6kIL$eD{4h&bi& zN!r9Avrr`0c?ix-yC82NfT+VJNSic(qf@@aK`?DcE6FrI@qjq3M*jGWsWx5OPQmA z_NB~Go?Xfuh1naBi*O)K&z`_>G{RmuBV!^v;m9AL!CEs)EXw`?HP(9~Ki~xzXpI&N zu8b9s=UJaIpM@ZAItp9&n4#AIz_@)#L|*n&bgAmWjv|}!1S;)WhwL)|ynE3g*01bP z9(1DU8!XDaEJ`=4q7IP35}eW}a?KR7E818BNUq@8CvrC{WH)Oh=TMu+at0N$J2jF< zZ=Fb!L-y?&Npo(QNMl0w4vpl5=C*O1&V=l}8p$cuu6$0YLiT=z%={BB)X1MqcCCkL zw+OntsfheYswvQ~CD0!N9bRXdY{|b7y%_E_GJgRu^13LR3J$TLG%~*r$(bv|lst}D zc049K93$@y%vZ8%O+i~g8kvmD9<*NOoOCia^eG|eNhfxkM&>an$aclf@hxN*vL`+W zlOhMr&lAIwVc&yuiUlucUIGt9o)m(9py{T_QKw-)1EhTfrwj6GbI>JSBTampywf1p zoQAF-YGfWoLqvWeT9z)6dNO|wk3_lzz-5s%;RVPwx&I)z#R&p#MNM<|i25%kt5|}I zIpQS#R?B`))N1Du*$d{t3|R79I$dV(+_N=s4Z*<&1-N5tkX_T^Ja#Si z&iz{lS8Xs}&Epc>-nnn9F?kKIw|fR}hLWCzlnHt4W^kB$?nNIIVEKFIGK&&72mb=; zU&^=+bbIH4n}fs9n|^7i1fRG$_#&#jCqz@Mokc-zrQEBarGsm$jr$Z33vwsrfsC&} zsD>fWWFD?~>w4s#Fp z7y>ljxagk3PU>?qLrt3a+0Z3&y6P3GDSt*amP? zfTP9w5?W#Jga_&F4TI@!R-Ufdq+C%q{A(8pYm9X=F@i@CHg#g-&)T^j#(w}mF3}H3 z7RICGx7vm!lcWu$0gebu7IQ#={{vj&ELlSvteq=`3B}5Uy5QGQ_~H}}q}I;bU=^;h z%N4ZI82BW_T%@1IoTJP#_l!daDk^=^80{@8ylAdeSRuA>FS?8YZtW~J27VSgX6T1K zH^s$tYcVt8c5^J4v5fsF?dIS*V|}vSv--Ghj&_|fa5M@|($AGnjjMP~tC%!iyM_bL zq($uIwTeksCaajDU1N;gk7gRLA6mt5E)H|g3X>3z;6&ySq-wGiUQnz&7Lf2F%c&N;&!J8(3x7R1NY69 z7O#*AR8GW&;&SK)buuRxxO&_{lKWyQy24052`+1wvrZLX9xqvS1xppKK_ zrs_4!njf#%WvqDEqR}&yh->0zeJ(B{Y)z9QO5G+-io@3BoZumXMLp9kUC(s>pw2v% z{eGd8QWO{V6oqlt2kHb)`-EG*%kk_R*>ubEsz|~uIeEIsH?pUwBEl`^H?#9e)NTQs za0@48>EmFf`9aogXPFVBOwKn!9nT4;FylhF!MslN-eqT*@uhf?>#`=PBGQb8tZ^j9 z&1lG)9d8}Z6H`Z_b<6`1+Mc51Cc@@-l*^6_9OGoyCBnu$7}4%beHmUh?~hRH;}=R_ zCIpN^pCAR?9~ql0;HwD%dpU2qB0>j$RtZQrm~V8K0&$hb4tM8=aCui2W&h4Gqk zo|pcwRLFfUV}g=d6L04?D3gl-P$$bEsmtv!p37MDL-1HTt8;Gzuu=jQE(d&YUMb=A zE=PR8wGu8h(n}EjW=Y1$*-}GMTv65#t*9hpoKj?@ZXo-+=^XyPsEQ;Uc166%yXj+P zkYreNnz7WsZ~qADUo@<8wc z65|bbAUN+!P{?u6=3G4W_YBD$LC%I+mE7`p9Y06898AD5dakHbX)b0~JAyN0J=VCK zb%DB2!s7ZjKNr;QOZ^0T60=m8z3eQr--?Sb4UUoGRlJo2$C4N~yDT^>ZZ?-C>GSE1 zO>W>U@8x0l%H;$L)CqEq2k-45qRgdh?wl#Bw>e(RZ^Vm~xl`4&(B(uBC6?-Xioh&$ zRb!UOz$|mCBz>jJxhVKsB^-0b&U2Z2tL$2rvR^bm;l}-P6+5Jm%b;7J3`)f1J2Z%E zQ*g}W0vpXG1f<3Vr023#UUo*Q!iX7hn0p3eGtcE+E>I`PX&)TGTi@n)#S_|pn;}^f z;j<}Tzyf5 zp$e$0oqQ7G6}dX8B3%4Vy^v;%am0;DeYZT(A}%*YK%T?}Dq3R{CpP2aRvCjq=AHNL zaPt6h=lu{EdFQ=$iFAj4$mO1nv7knt}P?umWEN#y0ZA4UpifOm7-$pTavJ z>ocp7HBxy0V^d}q@?Hw>etatP>&Qn^xc9az^BD5EDZKOXUzvYJKAOV&9=PpcW1B05 z+iPbsd55_mh4(xLWi3X&FokzK7G+f;UzEc89hD*T73j7^?nlh=kpU}J zpA4rQJYt68%tkJi=AEmE;nb5%i~ZyzVYicqHSzNZ)$x`@N=Nz0j3dLj7!>C^oW!KjX=jdGgV}7)l z+IyFIQz5O(N0PHonYwukx z%-Lsh*WSA(^c9rZZ*te(D^K}7fCDCX?Y#=QYj1Kp-rL~u9`3V7h`aWoVBT$LqQTs? z_by0xxohu4L3fBwWA{U=(w86uB%j4_Ux!W3Qs%g8@9@D~!N@PP3&w22Vbf+2k*#*^ znRmKya=EoTBm>uB&|x1D_5$2;4ZMh}&I}yD*$P|_suTDM&R*aIFp%p9@Cebvc?c-6s~-`&`r({y9ND?6 zA0C)40^j7Wes~ZzG+C6p`r$!C2zVxYmT<0M3!B`F3Fi)EnTW}KnsA<=YME?CDBOUb^9#1Dio9H96M`UzqwU zm{wtOS3g`Rs#I;#r{Ot+ZB(kxY3XTfdhuUBbj^RCskhBHqyaYY~ zhwPoI0a?E%eMItB?I3v1+5{`C06Y^l!q*^Bc1^v6jF=e61QVkM@Y!6z?;1U4tot3t)#)6uto*+p3mJ1y{xOT!=6}foO9NZ%zx#7Z0OYduQ)f;iAQ}Dc9b4Vyh6fREV;7-hOj%3fi)Gh$y#n z$IZd_;gK>Cn)c2Qw+5HMich3oL#aFW-W;S+<*AJV+<)_^=armc6s{W<(`JKaKp9t1fH-Z`H7EggGyo^T(wWU>Kj4 z3F`Z2n!+%fBkq1;-YTj)XO?y&TWZcF$t^XfOtqBF4sXu2?5C`qONC$N#a$f!1*$sb z4yCXvuCNhTlu$TjcyhZpMQ7um-jb)4%h-D6ViZ zdNEPoVIrWkgNKQD!fu$&`VLzmYq8SiUfHl(F~P(A3SM#}5-L82I%V%&B4u9{SMjt~ zk=;i{b{`eleN<#?6*g;_Gb*`;IfaD8-aqvZjO!o!AY$9BwagPxtrsnG<7SMjC2@ol zwSK&3)>&3MDu-u+>#Y=)$*DK4wNjsAiTrl#o>|VHVLE#g;}kMn9i2q|hlthDeX8j!Z8?2Naa6qo7splw6 zfm}TWuzGq3p#45iiy=WQq3o5+aqPC&F~@=1u3(PCv3)Ib9Hs3x=EP0($FQW~WGEQQ zz8lEEall3(1!vPp%f|9t5Qk-dm&AH(I~ajm1aW|PuqZe=djQN!;e$m%JXo{@fLt>L z@nF$LK%Q7N1@U0fZGa-f`Cw5H4;JyK5V_&OdQp*(r5lbys2;^F7xk4anspSfRd`m4?2ozXqxk8}OQp*(rMV4Bw5Qtd| zaA{zP@HrIssf-u;$ZuJD>WU?neC zEVW!Yi89X`o?5O9LKBThUar{x3A#;mI{OA%l_9%l@#7A$T(N%vtXQttzeP?k{!=sJ zAtN?z4iSUYa)o*Cr??Qp4`wl%ym~BC!_Ub4L0m536CywG4&qZy0i^vF0I$^WGW$x* zNCLm*m1?9_;!+jpO1qKhdXzT}yA^q{8cMrBGc0otkzp@jGw>ZqvjSaUb^@c3_X3YX zULe2m2zZuy(1~nei<1C6=tLl5aqz~2P6To+4y<_4i9oKU z9(1Cbp%!Ohc+iQInMOLReFyNOOrRk&#4?39$xKis%*6%_woK{%sK_^zJ+ENcXokqp# zEcKw%w;_73r5<$RBOLoI^`H~uLM@4GR$45H>>lQ5qrHnc8ff3goOsabA?Cz`PTxgt zR4NQIQ$~mEXgdtRk-7q55#H;X9T3jvluhb4X8=k%jVBmC8L6uo^uKe(<1Z8Ag)+#+ zUd9w-V`}nh>SA(zgbT*}6BtGtsn;VY!;i}6g6c>d;wya_FQ7m`;BW1t!^pi4h4bBA z8f)av2jXG-YedjXM(XXfFvk_O;q5ww=Ho)?>T#i$h^_{+k;ey&SIJqJ<5;UuGE40T{aCLXpcxriZ%{cvYE{7`bmLxQ&683jcp zl@cm7A`{@z<@!ZaS4){#SzHX`h0x`rq|j0!boqQKv_w3JxO_Du7SDJ%OCWT4m4sr> zLx3(BE1{*%LKuF@d(0x<2=&lg&bK3i{|N9V6DZK!^7N@j;=yj zG;b&^!s9|)g-P>f(LCo)D6czaQ3A%5t{@K7Zgx@BoYrhFcO&O>ig+i=M!k;7YVnU~2Xd*Wt30hU?LK<1-k4yWLms(Bx|S+r-i zima0HYvC+DH65}~RZ3Gs_aMv$rm|*51J)zm_}?4J#$SlKJOG67PN;CkPim$YkE00ir!! z{Pa@HTpW3)$Ne^l>2E_~#7*`74j?!ZoRRQWWR2j8VoVQh-s6^o-ei(c#LNC5ACO1rcwmp9aeSG-UuH z;riE<@oUQXHD&yoGH^{9xPTaG_SH}XQS3oXxvVzjvf7ku=#*W!)^hLXWh{Qs`_=S= z$Qt7Jybp@J{5kK}hcl=mhP@3`vFuL~Kjb~04YVlviYm!F%C82-l1==I_cf81KjJ+x zgqz^x5h43Ta``aAO_&oh>Up!;yxwC#ear>1+R!t;xrkA&9H7T(pmU7JMruM z$e>I!E5lA7l0G&)o1?}Q!%knAM3QTIYX{V>A3TY!f zlER|U$eGZ5kp2q(0gYnyD-6xWSc?+^iV>kvQMbRm=;%|=-GoL zJM1TWjsp`eN==20vQk72P$Cu2z?n&PyxmN-a_OqR23g5G3-idLeFqzr?@-8y3|c!V z;eNw(g;y<6PfTd+cndgw&$_NOL)4540s=A8md+w3D6B}voZSio%<*}wWQuO6)G2TQ zD(}u23DWXx#6qwr_a?yW`P|W~95+HJ6|8t6@y&8`cx! z)kd6;ha*{)3kDeD;n7KbX?=rYvI+rFtS@PbaIWed=-a9Ba1ox>O~VVwAK&;Xis*7= zR9lItcH-BBt7A5x14Ko961SHK*-)bPl$@eF7qnYQ_DQMwY6zTyk&}wr9@QIxbdnFS9Q%-k^ssJI^L`LLoBIxl~HCUqU;>8m53xS z;Z8n%0sWJywS%~VI6R3eIiKozM?Nl*RhtNV=baXlg1`OQ+Cjq zqV5QuxFuOK-m>Enp*y}D8G#twRD>dC#h^Q|Z!8TGl~WBPJJ?)pLRgIwG8%NpIWmLl zlX_0kV97=eIXk9@CSrP+jA^{}BGJyAF^ouf9cut_$z#egvTxv`>r4*Z(P#`~Ox*dx zuFsgNcSDGs9is9YAE)DTJgdJvd!`t}70o-_Z6^N^YtHTNZ z1Z79$%0^>NJ?&tRZJfk8mE zP4j$qRR+z8C=(Iw;GLO#RdG~E;@BV_594cdj~Wp-rB;?Wna8Z&yqG&|f@ zXPGF?$v=g2TjHF;LtIGL;h&3`ZTR1g|Ig$95&S=k{}=Et6sK@uZN5hEZJg}C@?^tC zCxnxogOkgZV1Rr+yBw+JA#k75xDfXXcuS-O|2N_Pv-s!A|7HBYjsHL5 zUl>1tZznT8!pY3#qD+AFlsyxmlW^ApfaXe^T)wc)<-;WA2Al$oI0diA$>`*X_ifGl zaq{*t_n$Zgzt5b@y6C66?FT&5O?%P}SqlcZ<_dfI6gT88a80kyo;Af?;H`Iqt#M** zf;eJ|Yd!9okK5DcyTL^9qsA0B=n=kBh)a`f*0db^q&+__dM3F>gX?0!H8LT0Qlh~3 z)8@ODSLT}5;vD-3KDw1;=5L^f`YHH5kSwnY!15gXPBWq9ziCXuwn!i7TXsTekC~A6 z?IZzDC$u=0eb0JS^=~jbCX7x&nYWbfYWW;og^B^A2~qePgK`X>&8)bn{lYgNVG{y>Nm;4PW67CF(YJ z#svyBc!irw)U6BOUg!>nx-KMm?A5&$Etwt9N4*@^ecwCkn(r@kT{g2dN>Rt%>Za7X z-mUI%!*a}8thkZf;tnfwhrH*8y4;BOmYdP$jwAXK$#jbw0`+}&{FAEa2w8NHEIPc* z9V&{N-jCeKM3x>eOA9_xdMLc{1WhS$2SZAiFe=^q57%pRN28i;Zd$3EzQ7&jWw{r4 zx4452x>0YSsDm{&+a2^**SyajHs5uTd3=eR@i>cw+IPC?dtL8+H?q+kc)#meld^$y zBlo${`@L_ut~EXzj1Rh5JKWGEZiY9~O^4<-H>->-F`&#Hv%{Toi#z>ccf@@6!uhCs zmOHr29ks(9ggU(G4!$*!AA>4c8{8qXW+Mqb<_Zs7;<|RqL2rc{MkAXmrlCrCZ=sp$ z+<{+q^V;1(IHPgjblrCE?YBXA+Z{;2%|mVwDvRu3WOIRBfy-d{+we{q_MG2#!_*e_ zrn_dDH4q)oJY3f7?buVFvl|NNZYa46V)rAup7cW~Z-{$r)2ML#TMzwMarl+A9yi@GT**^CH8Ttp9ks!c?Fw9B2;=H@@{UT_rN zxle{@ewQ0PO1$}a5xB}+$u$mK_mPXpoe5oR=AmMAj4A4 zY`0otO?Nc450}?nDFQ3!VbMvKdH1t|(oK`mA0DM6N4n$S$ouHX>A-n!Nu=bO{w;S_ z+&v>L8v(N3(N|V3~Z5%@8L>kjWv-WCWRvAdB%O!pKg9kr=2L zMm9$X_J0M`?qffIYW(!1{J60ja=D-c3 z_p+;)if(nPI}9Q5R*^Lsm-1V#`%7zdj(Zu(*j~LmlqvcG0lqKJ-*V?L#fuIldWV3} z<+#k)H=<3DF$1@_X21>sf*9A^D59>&njw^=A9r)1${Lf6>-SrNonjC~nMfE!6y~(^ zWKCxU9O|QB2fbV1y+8*ld(?HiAa*8#d6a0ai&Seh(X;28>jl|bytvt7Y%M+Kh|e`r z--2gNaT3@1J9M;|X1tFa!V9N(GXydL+POT5w$Qg+?2viTwcf@+p6#Y~xfc3QSZ+@h zLkHX$AoQ|?j<_j=rbzcqb+N(k*z4RJBp6N1YrSi4aRYC;-u15Sed~QUyaN43m+NeF zQy+H+UF8lYU#;v`ap_@7P=+fGG`J>Zqi7-P2CSvwTwE4n=&BW<2_Kia_C`15J~wo| zYc3E|xrOi?YC=(sU3y0%V*D=+S*px(l+G^W9o+X`iAPM8yo63R84Jd zsZzPB)^-E0laDUCB07Bvey?6G_>I=4s_QDNt6R$JTk%2kIM7(x(rlEMx8sH1Gp3j0 zv+nhcv&(B6n($fl+9baAM#VI3Hkt4l`L-zE5nkL-J9}pNrFGRUNiv%|q?p#Krsf*) zp>=#Ny|!{weZ!5az?Drc)kdQzMSaZ>2l>pl8&F=^W~{!V`10~4SClVXx#;5ZOD`*5 zU3|rwMXT48Uv_DE$?D?bRV8c67cXCvP}xY5i>s`vtAuAz zZgWjblhIh-Sh=aDb#uaOR2kUnn${{}rF7W@(p@4ibV7V}Wm~24eOv*n8gI?&2BUm& zQ&WSX3}sX4mab`Pgr>F|n`=IK?fRG@?yD$!L>s6!LR zT|HyE(b8J;$?~dJL{{a-*76PYNz_6x;;Z)MZIv4n#p@eI3@@2B&Hq(|=;Te6Emd_3 za4}4tJrht{dt=RljWvxmE%jB=$s41SFPk2n+}c*XU}IxDichXZS~zENePcs?V-3K@ zrpe6>ZOGJgRMj-M)i*V^0>DL5b=~BuhRW8u$;~ZIZ8cS-fKXMN1TUy(s4#n9Vmjo%tq;=Z#)Y8pHm$*J}3;!C`(>&{S&u zA($G{M1_TBzuOi1#GTKE!(Ja5gEl#wT4mlD&3F5)tn*mA*UsCG&U|)~H4BXuH)(^- zMT<((Y7Lzq9Hrw?)=DNlNpqOD;Yf}e$61O^D*OEgy5+4-+Jt2L&S@Sq2eycqibQ-rMZR98c7<71Y+sQ*-v!1Xi6#7GGFd6jG5wc;L9cSne zq=0rMZ_kouWp8!w(jLwkK6}3SUzi7z1N12PCXlBbk+6F>s27L0 z^1nC-CnQ@=3Yl*vC#}OY?+-d)@=XY?UcwQ(?!QuEkt0+yj?H~!`E0s$mVS^rQWXrd zPDZIy_#Sklu+Sc;8&fC)D;)Gk?Yuuso5M_6#kCenj{)m<#@(0%C|tO3A)*W53ZOBV zr}QR!kFTa{wJY_?9;~L8R<2yD?$oYqRj%OY%2s7JBDDg&nF2P{3p_f-3xxeXbP9)4 zXbO9f8T&#GU@7P!=#IA|MCxaX96Vq-5!-@J#;~wZtCao`T+&|P zURkYfR_oW(dhP1kWic)2J3Y*DYq*Fe)gUy(s>t9BaH+IN6&2)xa z;c3v)0N^jnhS^AZY}j>1P+H#3;X2CZoIwssM4#q=%ThW0$eVdJo(mnx3ecqZt1IWu zom)zd8=bM!p?S<%eu76?M}z^ILU@nTrz>$!qB&T{4He5Hv865OV|Nk}f@ZNQviGPl95xfOS5}I*kE`p^yN?dKmMA5QwU$ zDgp34(R=DT*(fTfF7X0AVn~tas*4fb>dKffYaAej35f)_UiX)foI{^s8%Eue)wVyu3fgFwN0TwVSwk-f}Xck2`AgTAfU$zNNw0 zz)XxJ9rrjxnPLV|sIP=ln#XLmK;i-gagf;HjO|jb=ZJTCee?LB}G%0^~e}w z^Pi|vidGC;+9xDLCn)*>d&Q)<=n`opSY?;8CWfG<{a+v{?ywVq;W$5IK!Izt8LmBw-0al3T*by93A#$Ka#Mbuo%M(_eoce4O|$Yih8pE5#5zD|a$z;) z!wo+w5mSF;Vi?6do%N}tB`v@T3lHVT<5ENxJHM< zO43h?Zapy%cfKw37So4a3`OuxQRV1q%MU;7d;}h3#{=gk;A602F=^DUw#iu+zZViw z258yo5DQ<@;|tT;Z^L)ei|L4W;GRvx{v&fQ(`d8P#`CJq075*D2jVLV@=Eov)8EI) z2O}#ylJ#vq_d+;(J}ycIMi^GZR>F{ZFc`vq4@eaPNJ2kW+BDV?ut-tZrt$*&IcOh_ zhnZVL&`D3EF&WuZ;*BVepSh9R0+-b4_onm{&ILl|-IH=+b2?>efF&5xv?*91y*Epd zfzcbNlk_-mU?tDy27Wi5;n022l0w}Iols#l9SXR1-8zN)LkX<~!g1U3lzOiu)4l9wR zMzzjRv>oA%-CAZP#MXP8ttjQN7(dq(wJ1Qb1p{w?e|UL_u$L zl(i3!@*8bm;L!!r~#kmC55;Z>v_X)~@)ah2*F|9%4Z> z#C0tAO!=m_QN3~rjqcUw_=t!|k@YHCxKgjkqp{P%=F0Wj)p}al*r=@4tFWk{JvzJm zt=jsoms`$g@q{_d4iZeuxYCUFfIjP3joY|-1%#mGR9r3c@tUemj`RngibZ*(h8eji z9VmyGUk;>{Jiu!WTzd0(*<7iwU1k$VIDv8(Gec)P9*dm9!*7;;zCd+t0@JXWFK@P1 zwrwq*m0nx&c#)@p9+$$wFqY7;GK7^E)}_1-5KGFO`FI@Ygr$^Wv$DR+ru6HL46;NP z<*!%FQhiw=2_7p_rBB7iR6S|1M(o53)HTcoZbpKzBmzbwEC67PXtX=4)OdzL-S#+YZ6lda-!l|Yiu|OTAR@7um zy{Nuvhp-|dYGba|wzjdH$~AJg`ZGafv$pm)j47(T?8Ak@pw-(4ZLBfHxKo73Tr1_f z*&Zhv;*k}5*j34gG_i6)O>?eT=BlV>^Ar><(;ssl*~(5JEk{nognLPToc2ZtUt#nZ z@{vAo<9Twxcos0m4AtF4D-()R7Meho1dK3VVthouWIjm=C9E<@H%;ZS8O09r;kd_8 z6DUc5js2h@<3O4UjW(azQ%FDJJSrVAW(){N;z56ahXx&l{Gr(cN$3Q_3Zef+Wi14v+ zXKB_s@R=R=fP*$YUW6HqOLT;P^F{nIB|bMwNZsvTvomgG4m}a!7mmOMoU0!$=H_r~)RdWKe@IjWD%c7z1XPO~I2HaYqXBM=QnRGg zOx%cqTqpM;%QTaNW-s^HG&e;wIw3j`+!KsMoD9;UwH8Z_7|fXidlAlakljZ}l6!2c z`I&ocbNRg?g6$Rh)_ZJ+5Z*?4vkN!pN zhYOJ0d=mfSD5GRM!v9^sr}6)XfRm1Gfalu-y#5w{gdZt=z8%5qYxpBPKF}SN3OqhQ z{v8U(D?)Pdr3+s33V%%DR_{+L{9_8&SFMwl!sA2zPZYkv*Q-g_CCSGW9)GvsR}>z9 zC*k)L9)EJ-1%=1ocz9Xi@rNT`Q~0ar9U~W&pZ`{P{1J>Z+(QR_#vk1H5rxNJ^7sjb z$DatPD?I)rNk`%F*G)dC@ISxc7&N_oM&a>CSw64u_{%RZC_MhC%-<{g#H` zz;Wb2GH8WxIYd4AuH>s94B-#FmDs0~i+ZNULz?|VfIk2`j1TvERPmpV@V68{KFB8( zcvvTYeP9Osmy}L?fb#Qz&rOm^K8gdI=3qaG+K-|9nbL`m=H{nADDRz!|Nm5Y@&QM| z^zb&kwk`UJ;NK7U16G-rUYG%YT;TU5@j=*6&cJ^P@KccM^+>K0r4yg`{0lSa{3hXd z-=En0&RG5Y4E!(6fWI^Y{;z<){qEC6$1=U~C_>s#eBS!^0xtO4FASew^_`~AYct?H zwS5}?$7jHwp8@{^z;PPc#P#9~{I3Z7z9c>z`aguu>E!9iPQDHHIgL+#ft7l&BYz%J z4tPWv^=7Y}*pX`+Gw8fm@#E9w`ZMr<4shC4++RL91OGQ>!2e_h{O<&QUlN~v_%*=i zCbWz?I#j`gfxC{j}1Fj|_Ys@KbocIQ3zV2yUNM{n%U2@7C-uD4h%I4zQ!}J4z?^ z1AhrP{ZgX--|XirLPraG$=7GV--Z{;sh>}*IOM;%%cVRPaGKpd2)Ou9owsaj9giyf zV#E(WR6LDNllb%-ksrP_1D_vfr9H%Y_|y#i-v)e6dGnJ$6@2vHD-QZz&3t(Vo!0=T zyiZ4R@f&;7WBrwXcvk$ji65?KUEGp*M4}~R3EXAdWs--UQpSG}D8*hq? zPq@Z~2}}k1^mMa=zR^Hx{`Li~xf_6sNnwaN+{Z~&hBYSGBaoWbwJM=$FkAm3 zun5xnZ)$jO+cN`@6`QE6$S`ECLEG9gnc#2Rbl~U7Ha{;jr0i#qimiUXNu--a3=N9$ zwt1UEqdjZXpRarL6KowROfuMsF#`+(b{jNc5sI&IDpP^Kuf%FoSQ(`E_S8#@DuRS0 zd_b|~iD3`0U+`LNkf2Q9rTFb@JOCOm1&}3aBtAgR*P0e63Mr7vyFZIMdy}Z7Yq?a~ptBCQ}Ha2!E z^|ZdSiZGw=Cd$wZn!r@f8$p6D#o3iuQ(mG%qwhBv3Pf0x@19(|v{_wUOYsUQUQ3DM zUq#;Xf^1(coCvo)q}BQs-i~p0nGNe(YaD~>+!+-(9B$I^H-Hpumux~9BOL$>e_ujn zIo)D<1%FGAvPv6y(Ai|GtVh;u%phQ5Ykiu@`;s2AZZ*&VECc;?OYET+ucc=f&T$y! zhzWf`kKBTDbW=ji1wVMN4PnpXr>Sr9trD?tbxfs@9xt+iqS$Ri^uh#7d~)gn_E4@c zi%#JOGubJCDRB5*aqx%%UhdmtJz#Sw1PuKT=4wnoNW|E5sb?~zEo$hDxuP!eX zzaPXMmJo~b7qq;k7Yj^xQ#{uyoMAt|{E3#g^j%STTQ4$jH7alMX-nq}QPa5m6M%pH zjW_xx{~fL0Qd=Lv^0Hhp{|!K~lu*cjN-MDRGy0n{Qg>F~;uUScSf$nfoFZ8Iapean za>e?83eYz^{x4u1ftRKG?q_2BTRoQk4D$cSE6)$-@UnE^eim(H{3}?_>Z6E8@6@-u z980$~5M}jSd6WOsKxg??S1h?P=WZ{jIqCvO}=-!Dqiu3q<7%`a8Js>$i1&OI4kQF1jjy;O2{3-t?c` z&|FRXk-)~kVkN(i8`f{-E$(FLH<{^O5xL|qkQbCcaL5Bq|A~h0UDkdE`Z8|r!=IVC z?ZdD%#<$;={v*oX@0GWGAC?*(P~@`Tmc9lk`M3HlK5pq{toQoYoc{y&-@qSj&&pdo z&r(yYsln>CcHk+Se*MpBe9zK{qWY}7rEiVOTYS*cQzY(PR?gDt0#j z&PBgZqdaMu{I-5+>%1nX6^qM%x7L5w5Hz*&asLInT$XZQg57JDB(`q^wOSN<30 zTx$PX{gyVh{QQe&)DI#^1V5S`Ak$G`kDMMmf$Uk literal 43280 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 diff --git a/compositor.c b/compositor.c index 0551a85..ffa0f8c 100644 --- a/compositor.c +++ b/compositor.c @@ -1,40 +1,33 @@ #include "common/shm.h" +#include "common/state.h" +#include "pointer.h" +#include "keyboard.h" #include "protocols/xdg-shell-client-protocol.h" +#include #include #include #include #include -#include +#include -struct client_state -{ - /* Globals */ - struct wl_display *wl_display; - struct wl_registry *wl_registry; - struct wl_shm *wl_shm; - struct wl_compositor *wl_compositor; - struct xdg_wm_base *xdg_wm_base; - - /* Objects */ - struct wl_surface *wl_surface; - struct xdg_surface *xdg_surface; - struct xdg_toplevel *xdg_toplevel; -}; +static const struct wl_callback_listener wl_surface_frame_listener; static struct wl_buffer *draw_frame(struct client_state *state); -static void registry_handle_global(void *data, struct wl_registry *registry, uint32_t name, const char *interface, +static void registry_handle_global(void *data, struct wl_registry *wl_registry, uint32_t name, const char *interface, uint32_t version) { struct client_state *state = data; if (strcmp(interface, wl_compositor_interface.name) == 0) { - state->wl_compositor = wl_registry_bind(registry, name, &wl_compositor_interface, 5); + state->wl_compositor = wl_registry_bind(wl_registry, name, &wl_compositor_interface, 4); } else if (strcmp(interface, wl_shm_interface.name) == 0) { - state->wl_shm = wl_registry_bind(registry, name, &wl_shm_interface, 1); + state->wl_shm = wl_registry_bind(wl_registry, name, &wl_shm_interface, 1); } else if (strcmp(interface, xdg_wm_base_interface.name) == 0) { - state->xdg_wm_base = wl_registry_bind(registry, name, &xdg_wm_base_interface, 4); + state->xdg_wm_base = wl_registry_bind(wl_registry, name, &xdg_wm_base_interface, 3); + } else if (strcmp(interface, wl_seat_interface.name) == 0) { + state->wl_seat = wl_registry_bind(wl_registry, name, &wl_seat_interface, 7); } } @@ -52,7 +45,7 @@ static void wm_base_handle_ping(void *data, struct xdg_wm_base *xdg_wm_base, uin { struct client_state *state = data; - // TODO ! + xdg_wm_base_pong(xdg_wm_base, serial); } static void surface_handle_configure(void *data, struct xdg_surface *xdg_surface, uint32_t serial) @@ -66,6 +59,61 @@ static void surface_handle_configure(void *data, struct xdg_surface *xdg_surface wl_surface_commit(state->wl_surface); } +static void seat_handle_name(void *data, struct wl_seat *wl_seat, const char *name) +{ + fprintf(stderr, "seat name: %s\n", name); +} + +static void seat_handle_capabilities(void *data, struct wl_seat *wl_seat, uint32_t capabilities) +{ + struct client_state *state = data; + + bool have_pointer = capabilities & WL_SEAT_CAPABILITY_POINTER; + + if (have_pointer && state->wl_pointer == NULL) { + state->wl_pointer = wl_seat_get_pointer(state->wl_seat); + wl_pointer_add_listener(state->wl_pointer, &wl_pointer_listener, state); + } else if (!have_pointer && state->wl_pointer != NULL) { + wl_pointer_release(state->wl_pointer); + state->wl_pointer = NULL; + } + + bool have_keyboard = capabilities & WL_SEAT_CAPABILITY_KEYBOARD; + + if (have_keyboard && state->wl_keyboard == NULL) { + state->wl_keyboard = wl_seat_get_keyboard(state->wl_seat); + wl_keyboard_add_listener(state->wl_keyboard, &wl_keyboard_listener, state); + } else if (!have_keyboard && state->wl_keyboard != NULL) { + wl_keyboard_release(state->wl_keyboard); + state->wl_keyboard = NULL; + } +} + +static void wl_surface_frame_done(void *data, struct wl_callback *cb, uint32_t time) +{ + /* Destroy this callback */ + wl_callback_destroy(cb); + + /* Request another frame */ + struct client_state *state = data; + cb = wl_surface_frame(state->wl_surface); + wl_callback_add_listener(cb, &wl_surface_frame_listener, state); + + /* Update scroll amount at 24 pixels per second */ + if (state->last_frame != 0) { + int elapsed = time - state->last_frame; + state->offset += elapsed / 1000.0 * 24; + } + + /* Submit a frame for this event */ + struct wl_buffer *buffer = draw_frame(state); + wl_surface_attach(state->wl_surface, buffer, 0, 0); + wl_surface_damage_buffer(state->wl_surface, 0, 0, INT32_MAX, INT32_MAX); + wl_surface_commit(state->wl_surface); + + state->last_frame = time; +} + static const struct wl_registry_listener wl_registry_listener = { .global = registry_handle_global, .global_remove = registry_handle_global_remove, @@ -83,6 +131,15 @@ static const struct xdg_surface_listener xdg_surface_listener = { .configure = surface_handle_configure, }; +static const struct wl_seat_listener wl_seat_listener = { + .name = seat_handle_name, + .capabilities = seat_handle_capabilities, +}; + +static const struct wl_callback_listener wl_surface_frame_listener = { + .done = wl_surface_frame_done, +}; + static void wl_buffer_release(void *data, struct wl_buffer *wl_buffer) { /* Sent by the compositor when it's no longer using this buffer */ @@ -98,7 +155,6 @@ static struct wl_buffer *draw_frame(struct client_state *state) const int width = 640, height = 480; int stride = width * 4; int size = stride * height; - int offset = 0; int fd = allocate_shm_file(size); if (fd == -1) { @@ -121,10 +177,11 @@ static struct wl_buffer *draw_frame(struct client_state *state) // memset(pixels, 255, width * height * 4); // Checkerboard pattern - uint32_t *pixels = (uint32_t *)&data[offset]; + int offset = (int)state->offset % 8; + uint32_t *pixels = (uint32_t *)data; for (int y = 0; y < height; ++y) { for (int x = 0; x < width; ++x) { - if ((x + y / 8 * 8) % 16 < 8) { + if (((x + offset) + (y + offset) / 8 * 8) % 16 < 8) { pixels[y * width + x] = 0xFF666666; } else { pixels[y * width + x] = 0xFFEEEEEE; @@ -142,25 +199,27 @@ int main(int argc, char *argv[]) struct client_state state = {0}; state.wl_display = wl_display_connect(NULL); - + state.xkb_context = xkb_context_new(XKB_CONTEXT_NO_FLAGS); state.wl_registry = wl_display_get_registry(state.wl_display); - wl_registry_add_listener(state.wl_registry, &wl_registry_listener, &state); wl_display_roundtrip(state.wl_display); - state.wl_surface = wl_compositor_create_surface(state.wl_compositor); + wl_seat_add_listener(state.wl_seat, &wl_seat_listener, &state); + state.wl_surface = wl_compositor_create_surface(state.wl_compositor); wl_shm_add_listener(state.wl_shm, &wl_shm_listener, &state); state.xdg_surface = xdg_wm_base_get_xdg_surface(state.xdg_wm_base, state.wl_surface); xdg_surface_add_listener(state.xdg_surface, &xdg_surface_listener, &state); state.xdg_toplevel = xdg_surface_get_toplevel(state.xdg_surface); - xdg_toplevel_set_title(state.xdg_toplevel, "Example client"); wl_surface_commit(state.wl_surface); + struct wl_callback *cb = wl_surface_frame(state.wl_surface); + wl_callback_add_listener(cb, &wl_surface_frame_listener, &state); + while (wl_display_dispatch(state.wl_display)) { /* This space deliberately left blank */ } diff --git a/keyboard.c b/keyboard.c new file mode 100644 index 0000000..bd0c877 --- /dev/null +++ b/keyboard.c @@ -0,0 +1,83 @@ +#include "keyboard.h" +#include "common/state.h" + +#include +#include +#include +#include +#include + +const struct wl_keyboard_listener wl_keyboard_listener = {.enter = wl_keyboard_enter, + .key = wl_keyboard_key, + .keymap = wl_keyboard_keymap, + .leave = wl_keyboard_leave, + .modifiers = wl_keyboard_modifiers, + .repeat_info = wl_keyboard_repeat_info}; + +void wl_keyboard_keymap(void *data, struct wl_keyboard *wl_keyboard, uint32_t format, int32_t fd, uint32_t size) +{ + struct client_state *client_state = data; + assert(format == WL_KEYBOARD_KEYMAP_FORMAT_XKB_V1); + + char *map_shm = mmap(NULL, size, PROT_READ, MAP_PRIVATE, fd, 0); + assert(map_shm != MAP_FAILED); + + struct xkb_keymap *xkb_keymap = xkb_keymap_new_from_string(client_state->xkb_context, map_shm, + XKB_KEYMAP_FORMAT_TEXT_V1, XKB_KEYMAP_COMPILE_NO_FLAGS); + munmap(map_shm, size); + close(fd); + + struct xkb_state *xkb_state = xkb_state_new(xkb_keymap); + xkb_keymap_unref(client_state->xkb_keymap); + xkb_state_unref(client_state->xkb_state); + client_state->xkb_keymap = xkb_keymap; + client_state->xkb_state = xkb_state; +} + +void wl_keyboard_enter(void *data, struct wl_keyboard *wl_keyboard, uint32_t serial, struct wl_surface *surface, + struct wl_array *keys) +{ + struct client_state *client_state = data; + fprintf(stderr, "keyboard enter; keys pressed are:\n"); + uint32_t *key; + wl_array_for_each(key, keys) + { + char buf[128]; + xkb_keysym_t sym = xkb_state_key_get_one_sym(client_state->xkb_state, *key + 8); + xkb_keysym_get_name(sym, buf, sizeof(buf)); + fprintf(stderr, "sym: %-12s (%d), ", buf, sym); + xkb_state_key_get_utf8(client_state->xkb_state, *key + 8, buf, sizeof(buf)); + fprintf(stderr, "utf8: '%s'\n", buf); + } +} + +void wl_keyboard_leave(void *data, struct wl_keyboard *wl_keyboard, uint32_t serial, struct wl_surface *surface) +{ + fprintf(stderr, "keyboard leave\n"); +} + +void wl_keyboard_key(void *data, struct wl_keyboard *wl_keyboard, uint32_t serial, uint32_t time, uint32_t key, + uint32_t state) +{ + struct client_state *client_state = data; + char buf[128]; + uint32_t keycode = key + 8; + xkb_keysym_t sym = xkb_state_key_get_one_sym(client_state->xkb_state, keycode); + xkb_keysym_get_name(sym, buf, sizeof(buf)); + const char *action = state == WL_KEYBOARD_KEY_STATE_PRESSED ? "press" : "release"; + fprintf(stderr, "key %s: sym: %-12s (%d), ", action, buf, sym); + xkb_state_key_get_utf8(client_state->xkb_state, keycode, buf, sizeof(buf)); + fprintf(stderr, "utf8: '%s'\n", buf); +} + +void wl_keyboard_modifiers(void *data, struct wl_keyboard *wl_keyboard, uint32_t serial, uint32_t mods_depressed, + uint32_t mods_latched, uint32_t mods_locked, uint32_t group) +{ + struct client_state *client_state = data; + xkb_state_update_mask(client_state->xkb_state, mods_depressed, mods_latched, mods_locked, 0, 0, group); +} + +void wl_keyboard_repeat_info(void *data, struct wl_keyboard *wl_keyboard, int32_t rate, int32_t delay) +{ + /* Left as an exercise for the reader */ +} diff --git a/keyboard.h b/keyboard.h new file mode 100644 index 0000000..382a598 --- /dev/null +++ b/keyboard.h @@ -0,0 +1,15 @@ +#pragma once + +#include + +void wl_keyboard_keymap(void *data, struct wl_keyboard *wl_keyboard, uint32_t format, int32_t fd, uint32_t size); +void wl_keyboard_enter(void *data, struct wl_keyboard *wl_keyboard, uint32_t serial, struct wl_surface *surface, + struct wl_array *keys); +void wl_keyboard_leave(void *data, struct wl_keyboard *wl_keyboard, uint32_t serial, struct wl_surface *surface); +void wl_keyboard_key(void *data, struct wl_keyboard *wl_keyboard, uint32_t serial, uint32_t time, uint32_t key, + uint32_t state); +void wl_keyboard_modifiers(void *data, struct wl_keyboard *wl_keyboard, uint32_t serial, uint32_t mods_depressed, + uint32_t mods_latched, uint32_t mods_locked, uint32_t group); +void wl_keyboard_repeat_info(void *data, struct wl_keyboard *wl_keyboard, int32_t rate, int32_t delay); + +extern const struct wl_keyboard_listener wl_keyboard_listener; diff --git a/pointer.c b/pointer.c new file mode 100644 index 0000000..7ce605e --- /dev/null +++ b/pointer.c @@ -0,0 +1,151 @@ +#include "pointer.h" +#include "common/state.h" + +#include +#include + +const struct wl_pointer_listener wl_pointer_listener = { + .enter = wl_pointer_enter, + .leave = wl_pointer_leave, + .motion = wl_pointer_motion, + .button = wl_pointer_button, + .axis = wl_pointer_axis, + .frame = wl_pointer_frame, + .axis_source = wl_pointer_axis_source, + .axis_stop = wl_pointer_axis_stop, + .axis_discrete = wl_pointer_axis_discrete, +}; + +void wl_pointer_enter(void *data, struct wl_pointer *wl_pointer, uint32_t serial, struct wl_surface *surface, + wl_fixed_t surface_x, wl_fixed_t surface_y) +{ + struct client_state *client_state = data; + client_state->pointer_event.event_mask |= POINTER_EVENT_ENTER; + client_state->pointer_event.serial = serial; + client_state->pointer_event.surface_x = surface_x; + client_state->pointer_event.surface_y = surface_y; +} + +void wl_pointer_leave(void *data, struct wl_pointer *wl_pointer, uint32_t serial, struct wl_surface *surface) +{ + struct client_state *client_state = data; + client_state->pointer_event.serial = serial; + client_state->pointer_event.event_mask |= POINTER_EVENT_LEAVE; +} + +void wl_pointer_motion(void *data, struct wl_pointer *wl_pointer, uint32_t time, wl_fixed_t surface_x, + wl_fixed_t surface_y) +{ + struct client_state *client_state = data; + client_state->pointer_event.event_mask |= POINTER_EVENT_MOTION; + client_state->pointer_event.time = time; + client_state->pointer_event.surface_x = surface_x; + client_state->pointer_event.surface_y = surface_y; +} + +void wl_pointer_button(void *data, struct wl_pointer *wl_pointer, uint32_t serial, uint32_t time, uint32_t button, + uint32_t state) +{ + struct client_state *client_state = data; + client_state->pointer_event.event_mask |= POINTER_EVENT_BUTTON; + client_state->pointer_event.time = time; + client_state->pointer_event.serial = serial; + client_state->pointer_event.button = button; + client_state->pointer_event.state = state; +} + +void wl_pointer_axis(void *data, struct wl_pointer *wl_pointer, uint32_t time, uint32_t axis, wl_fixed_t value) +{ + struct client_state *client_state = data; + client_state->pointer_event.event_mask |= POINTER_EVENT_AXIS; + client_state->pointer_event.time = time; + client_state->pointer_event.axes[axis].valid = true; + client_state->pointer_event.axes[axis].value = value; +} + +void wl_pointer_frame(void *data, struct wl_pointer *wl_pointer) +{ + struct client_state *client_state = data; + struct pointer_event *event = &client_state->pointer_event; + fprintf(stderr, "pointer frame @ %d: ", event->time); + + if (event->event_mask & POINTER_EVENT_ENTER) { + fprintf(stderr, "entered %f, %f ", wl_fixed_to_double(event->surface_x), wl_fixed_to_double(event->surface_y)); + } + + if (event->event_mask & POINTER_EVENT_LEAVE) { + fprintf(stderr, "leave"); + } + + if (event->event_mask & POINTER_EVENT_MOTION) { + fprintf(stderr, "motion %f, %f ", wl_fixed_to_double(event->surface_x), wl_fixed_to_double(event->surface_y)); + } + + if (event->event_mask & POINTER_EVENT_BUTTON) { + char *state = event->state == WL_POINTER_BUTTON_STATE_RELEASED ? "released" : "pressed"; + fprintf(stderr, "button %d %s ", event->button, state); + } + + uint32_t axis_events = + POINTER_EVENT_AXIS | POINTER_EVENT_AXIS_SOURCE | POINTER_EVENT_AXIS_STOP | POINTER_EVENT_AXIS_DISCRETE; + char *axis_name[2] = { + [WL_POINTER_AXIS_VERTICAL_SCROLL] = "vertical", + [WL_POINTER_AXIS_HORIZONTAL_SCROLL] = "horizontal", + }; + char *axis_source[4] = { + [WL_POINTER_AXIS_SOURCE_WHEEL] = "wheel", + [WL_POINTER_AXIS_SOURCE_FINGER] = "finger", + [WL_POINTER_AXIS_SOURCE_CONTINUOUS] = "continuous", + [WL_POINTER_AXIS_SOURCE_WHEEL_TILT] = "wheel tilt", + }; + if (event->event_mask & axis_events) { + for (size_t i = 0; i < 2; ++i) { + if (!event->axes[i].valid) { + continue; + } + fprintf(stderr, "%s axis ", axis_name[i]); + if (event->event_mask & POINTER_EVENT_AXIS) { + fprintf(stderr, "value %f ", wl_fixed_to_double(event->axes[i].value)); + } + if (event->event_mask & POINTER_EVENT_AXIS_DISCRETE) { + fprintf(stderr, "discrete %d ", event->axes[i].discrete); + } + if (event->event_mask & POINTER_EVENT_AXIS_SOURCE) { + fprintf(stderr, "via %s ", axis_source[event->axis_source]); + } + if (event->event_mask & POINTER_EVENT_AXIS_STOP) { + fprintf(stderr, "(stopped) "); + } + } + } + + fprintf(stderr, "\n"); + memset(event, 0, sizeof(*event)); +} + +void wl_pointer_axis_source(void *data, struct wl_pointer *wl_pointer, uint32_t axis_source) +{ + struct client_state *client_state = data; + client_state->pointer_event.event_mask |= POINTER_EVENT_AXIS_SOURCE; + client_state->pointer_event.axis_source = axis_source; +} + +void wl_pointer_axis_stop(void *data, struct wl_pointer *wl_pointer, uint32_t time, uint32_t axis) +{ + struct client_state *client_state = data; + client_state->pointer_event.time = time; + client_state->pointer_event.event_mask |= POINTER_EVENT_AXIS_STOP; + client_state->pointer_event.axes[axis].valid = true; +} + +void wl_pointer_axis_discrete(void *data, struct wl_pointer *wl_pointer, uint32_t axis, int32_t discrete) +{ + struct client_state *client_state = data; + client_state->pointer_event.event_mask |= POINTER_EVENT_AXIS_DISCRETE; + client_state->pointer_event.axes[axis].valid = true; + client_state->pointer_event.axes[axis].discrete = discrete; +} + +void wl_pointer_axis_value120(void *data, struct wl_pointer *wl_pointer, uint32_t axis, int32_t value120) +{ +} diff --git a/pointer.h b/pointer.h new file mode 100644 index 0000000..c78d63b --- /dev/null +++ b/pointer.h @@ -0,0 +1,19 @@ +#pragma once + +#include + +void wl_pointer_enter(void *data, struct wl_pointer *wl_pointer, uint32_t serial, struct wl_surface *surface, + wl_fixed_t surface_x, wl_fixed_t surface_y); +void wl_pointer_leave(void *data, struct wl_pointer *wl_pointer, uint32_t serial, struct wl_surface *surface); +void wl_pointer_motion(void *data, struct wl_pointer *wl_pointer, uint32_t time, wl_fixed_t surface_x, + wl_fixed_t surface_y); +void wl_pointer_button(void *data, struct wl_pointer *wl_pointer, uint32_t serial, uint32_t time, uint32_t button, + uint32_t state); +void wl_pointer_axis(void *data, struct wl_pointer *wl_pointer, uint32_t time, uint32_t axis, wl_fixed_t value); +void wl_pointer_frame(void *data, struct wl_pointer *wl_pointer); +void wl_pointer_axis_source(void *data, struct wl_pointer *wl_pointer, uint32_t axis_source); +void wl_pointer_axis_stop(void *data, struct wl_pointer *wl_pointer, uint32_t time, uint32_t axis); +void wl_pointer_axis_discrete(void *data, struct wl_pointer *wl_pointer, uint32_t axis, int32_t discrete); +void wl_pointer_axis_value120(void *data, struct wl_pointer *wl_pointer, uint32_t axis, int32_t value120); + +extern const struct wl_pointer_listener wl_pointer_listener; diff --git a/protocols/xdg-shell-client-protocol.h b/protocols/xdg-shell-client-protocol.h index 83d5a7d..026562a 100644 --- a/protocols/xdg-shell-client-protocol.h +++ b/protocols/xdg-shell-client-protocol.h @@ -5,7 +5,7 @@ #include #include -#include +#include #ifdef __cplusplus extern "C" { diff --git a/protocols/xdg-shell-protocol.c b/protocols/xdg-shell-protocol.c index e68e61c..5af53fe 100644 --- a/protocols/xdg-shell-protocol.c +++ b/protocols/xdg-shell-protocol.c @@ -30,7 +30,7 @@ #include #include -#include +#include #ifndef __has_attribute # define __has_attribute(x) 0 /* Compatibility with non-clang compilers. */