From abb50ef4161a091b0bdc73e9d95c3c6b93d21bee Mon Sep 17 00:00:00 2001 From: Gustavo Sverzut Barbieri Date: Fri, 12 Mar 2010 13:55:11 +0000 Subject: [PATCH] Initial BlueZ stub module. Initial module written by Gustavo F. Padovan, I changed it quite a lot to change some issues... but still many pending, see TODO at the start of e_mod_main.c SVN revision: 47160 --- configure.ac | 11 + data/themes/default.edc | 257 ++++ data/themes/images/Makefile.am | 3 + data/themes/images/bluetooth-hidden.png | Bin 0 -> 19296 bytes data/themes/images/bluetooth-inactive.png | Bin 0 -> 11987 bytes data/themes/images/bluetooth-powered.png | Bin 0 -> 17744 bytes src/modules/Makefile.am | 5 + src/modules/bluez/Makefile.am | 33 + src/modules/bluez/e-module-bluez.edj | Bin 0 -> 22035 bytes src/modules/bluez/e_mod_config.c | 173 +++ src/modules/bluez/e_mod_main.c | 1358 +++++++++++++++++++++ src/modules/bluez/e_mod_main.h | 146 +++ src/modules/bluez/module.desktop.in | 6 + 13 files changed, 1992 insertions(+) create mode 100644 data/themes/images/bluetooth-hidden.png create mode 100644 data/themes/images/bluetooth-inactive.png create mode 100644 data/themes/images/bluetooth-powered.png create mode 100644 src/modules/bluez/Makefile.am create mode 100644 src/modules/bluez/e-module-bluez.edj create mode 100644 src/modules/bluez/e_mod_config.c create mode 100644 src/modules/bluez/e_mod_main.c create mode 100644 src/modules/bluez/e_mod_main.h create mode 100644 src/modules/bluez/module.desktop.in diff --git a/configure.ac b/configure.ac index 056f18127..f0c68b0ed 100644 --- a/configure.ac +++ b/configure.ac @@ -449,6 +449,14 @@ define([CHECK_MODULE_CONNMAN], AC_SUBST(ECONNMAN_CFLAGS) AC_SUBST(ECONNMAN_LIBS) +AM_CONDITIONAL(HAVE_EBLUEZ, false) +define([CHECK_MODULE_BLUEZ], +[ + AC_E_CHECK_PKG(EBLUEZ, [edbus ebluez], + [], [EBLUEZ=false]) +]) +AC_SUBST(EBLUEZ_CFLAGS) +AC_SUBST(EBLUEZ_LIBS) AC_E_OPTIONAL_MODULE([ibar], true) AC_E_OPTIONAL_MODULE([dropshadow], true) @@ -509,6 +517,7 @@ AC_E_OPTIONAL_MODULE([conf_scale], true) AC_E_OPTIONAL_MODULE([gadman], true) AC_E_OPTIONAL_MODULE([mixer], true, [CHECK_MODULE_MIXER]) AC_E_OPTIONAL_MODULE([connman], true, [CHECK_MODULE_CONNMAN]) +AC_E_OPTIONAL_MODULE([bluez], true, [CHECK_MODULE_BLUEZ]) AC_E_OPTIONAL_MODULE([illume], true) AC_E_OPTIONAL_MODULE([illume2], true) AC_E_OPTIONAL_MODULE([syscon], true) @@ -641,6 +650,8 @@ src/modules/mixer/Makefile src/modules/mixer/module.desktop src/modules/connman/Makefile src/modules/connman/module.desktop +src/modules/bluez/Makefile +src/modules/bluez/module.desktop src/modules/illume/Makefile src/modules/illume/module.desktop src/modules/illume/keyboards/Makefile diff --git a/data/themes/default.edc b/data/themes/default.edc index a24d1f1fd..dd034d4a6 100644 --- a/data/themes/default.edc +++ b/data/themes/default.edc @@ -8393,6 +8393,263 @@ collections { /* begin the collection of edje groups that are in this file */ } } +///////////////////////////////////////////////////////////////////////////// + /*** MOD: BLUEZ ***/ + + group { name: "e/modules/bluez/main"; + images { + image: "bluetooth-powered.png" COMP; + image: "bluetooth-inactive.png" COMP; + image: "bluetooth-hidden.png" COMP; + } + //max: 128 128; + min: 16 16; + + parts { + part { + name: "eventarea"; + type: RECT; + mouse_events: 1; + description { + state: "default" 0.0; + color: 255 255 255 0; + } + } + + part { name: "state-clipper"; + type: RECT; + mouse_events: 0; + description { + state: "default" 0.0; + color: 255 255 255 255; + } + } + + part { name: "state"; + type: IMAGE; + mouse_events: 0; + clip_to: "state-clipper"; + description { + state: "default" 0.0; + aspect: 1.0 1.0; + aspect_preference: BOTH; + fixed: 1 1; + image.normal: "bluetooth-inactive.png"; + } + description { state: "powered" 0.0; + inherit: "default" 0.0; + image.normal: "bluetooth-powered.png"; + } + description { state: "hidden" 0.0; + inherit: "default" 0.0; + image.normal: "bluetooth-hidden.png"; + } + } + + programs { + program { + signal: "e,changed,off"; + source: "e"; + action: STATE_SET "default" 0.0; + target: "state"; + } + program { + signal: "e,changed,powered"; + source: "e"; + action: STATE_SET "powered" 0.0; + target: "state"; + } + program { + signal: "e,changed,hidden"; + source: "e"; + action: STATE_SET "hidden" 0.0; + target: "state"; + } + } + + part { + name: "e.text.name"; + type: TEXT; + mouse_events: 0; + effect: SOFT_SHADOW; + description { + state: "default" 0.0; + color: 224 224 224 255; + color3: 0 0 0 64; + align: 0.5 1.0; + rel1 { + relative: 0.0 1.0; + offset: 0 -20; + } + rel2 { + relative: 1.0 1.0; + offset: -1 -1; + } + text { + font: "Sans"; + size: 8; + align: 0.5 1.0; + text: ""; + min: 1 1; + } + } + description { + state: "hidden" 0.0; + inherit: "default" 0.0; + visible: 0; + } + } + + programs { + program { + name: "resize"; + signal: "resize"; + script { + new x, y, w, h, tx, ty, tw, th; + get_geometry(PART:"eventarea", x, y, w, h); + get_geometry(PART:"e.text.name", tx, ty, tw, th); + if ((w <= 32) || (tw >= w)) + set_state(PART:"e.text.name", "hidden", 0.0); + else + set_state(PART:"e.text.name", "default", 0.0); + } + } + program { + signal: "e,changed,name"; + source: "e"; + action: STATE_SET "default" 0.0; // show so calcs take effect! + target: "e.text.name"; + after: "resize"; + } + } + } + } + + group { name: "e/modules/bluez/tip"; + images { + image: "bluetooth-powered.png" COMP; + image: "bluetooth-inactive.png" COMP; + image: "bluetooth-hidden.png" COMP; + } + min: 200 84; + + parts { + part { name: "state-clipper"; + type: RECT; + mouse_events: 0; + description { + state: "default" 0.0; + color: 255 255 255 255; + } + } + + part { name: "state"; + type: IMAGE; + mouse_events: 0; + clip_to: "state-clipper"; + description { + state: "default" 0.0; + aspect: 1.0 1.0; + aspect_preference: BOTH; + fixed: 1 1; + rel1 { + relative: 0.0 0.0; + offset: 10 10; + } + rel2 { + relative: 0.0 0.0; + offset: 73 73; + } + image.normal: "bluetooth-inactive.png"; + } + description { state: "powered" 0.0; + inherit: "default" 0.0; + image.normal: "bluetooth-powered.png"; + } + description { state: "hidden" 0.0; + inherit: "default" 0.0; + image.normal: "bluetooth-hidden.png"; + } + } + + programs { + program { + signal: "e,changed,off"; + source: "e"; + action: STATE_SET "default" 0.0; + target: "state"; + } + program { + signal: "e,changed,powered"; + source: "e"; + action: STATE_SET "powered" 0.0; + target: "state"; + } + program { + signal: "e,changed,hidden"; + source: "e"; + action: STATE_SET "hidden" 0.0; + target: "state"; + } + } + + part { name: "e.text.name"; + type: TEXT; + mouse_events: 0; + effect: SOFT_SHADOW; + description { + state: "default" 0.0; + color: 240 240 240 255; + color3: 0 0 0 64; + align: 0.0 0.0; + rel1 { + to_x: "state"; + relative: 1.0 0.0; + offset: 10 20; + } + rel2 { + relative: 1.0 0.0; + offset: -10 40; + } + text { + font: "Sans:style=Bold"; + size: 16; + align: 0.0 0.0; + text: "Bluetooth Manager"; + min: 1 0; + } + } + } + + part { name: "e.text.status"; + type: TEXT; + mouse_events: 0; + clip_to: "state-clipper"; + description { + state: "default" 0.0; + color: 16 16 16 255; + rel1 { + to_y: "e.text.name"; + to_x: "state"; + relative: 1.0 1.0; + offset: 10 10; + } + rel2 { + to_y: "e.text.name"; + relative: 1.0 1.0; + offset: -10 25; + } + text { + font: "Sans"; + size: 10; + align: 0.0 0.0; + text: ""; + min: 1 0; + } + } + } + } + } ///////////////////////////////////////////////////////////////////////////// /*** MOD: CONF_EDGEBINDINGS ***/ diff --git a/data/themes/images/Makefile.am b/data/themes/images/Makefile.am index 0834b44ac..faf9681ac 100644 --- a/data/themes/images/Makefile.am +++ b/data/themes/images/Makefile.am @@ -35,6 +35,9 @@ bd_top.png \ big_arrow_down.png \ big_arrow_up.png \ bluetooth.png \ +bluetooth-powered.png \ +bluetooth-inactive.png \ +bluetooth-hidden.png \ bnw.png \ bt_base1.png \ bt_base2.png \ diff --git a/data/themes/images/bluetooth-hidden.png b/data/themes/images/bluetooth-hidden.png new file mode 100644 index 0000000000000000000000000000000000000000..6e4e515e06b9ceacfb3ad34d069e81e79ea215df GIT binary patch literal 19296 zcmV)zK#{+RP)x5W<#_gb9Hp%rM!I0|5q@`2|QOKms8UCk`fnAsGk|B8RZpU~C2?j5pcV z(vn*0?QM5CRrUMhzHW7^x7Xd0u_=9y-n;$oJzITGZD+Zts_@fDh=_Cla(X*&nDzVH z7U+bzkLyoqN8wLHyG}8Cc3W;h*wgvCZ(ry4>$}YH=Hg@5t|G%d0H;853K%~Ppm@vz zU@p^VGkYfj0PG&!j{pGm3x*K@AQ>O&+`kh50Mf?MIYT_GZ0K~veu!$rGo4bsXG8%2 z_73e-fSYH5VlE&at6+Fc0ia-dr@nhSB7EOI1h^h{kM76%1;bb{dMBhCj-szu(Kp8t zU^7T<9ESlQZ^twFaZ*PB025VbI>E(l7y$qpZ>lZV6ac^;t7}-7Cc(60oeDCgu&kz;s*Msg^_GqeZdE(pR9mfUcyvso+OWF^AO`i>92I~z zP;#tER(xXSl~S)s*$O4OIap>rMjNcm1H-QUv(&_+TPi&20I;x_ z--)|-4dLj%TPfYpkI7L_>uY69Yz^Cf+>}j)a`F^YOc^(gr-;H*K#WX=fz%olU~){T zmQdNCKt{96B~U?Q6}@QgMG1_UFq6D()JP_&8gWKBkwmN*iJWkQorF9ok+*s>3&b9) zYwwm&76Zbg5d@DK04zGT12pn&Z1_P$QdzckkzP)AtF>|QZfi49r;X(q>6wd z3PPkTtYQp!5nx61s@@w_uikl8@k)@h2nWL6r%ld%=tHC2w#uam;YES)D6SyWr$uOlkj4#)WX(u+d&f2oEC}IIk0BRhdI3NnpI13B`pgI5v0MH_!Lq#j71{IBx z8U-R!K|m`-@r)t@h{TA*O3JFj1msoOgJOPyH3QKcR5GxXYnplxn<-|V#rvFd zAN$Zx17XUrl$&LVYU6sxcugK{YT+jafQ3i>5RUE}rDnNFJ#47eM<#3~*0{i$9%mB{ z967d5T_~!7stMIfV1z<75UNn9q#-E;01%?HNS2I{AruKwBtZ4ymcF=mQ(shDSB-i% z^p#4^5+AFlCl8J%WA_}ZpBPJ|t^k>;h~^@a6EBdP{K+nFgzK zrq_Z=f{;QdDxo?TkV-6IBLa;CC_>a&z(fQPVPK6amra%qJoViE3$DI+c-wHT)YDfE zjryb=MWJLAtE9m03rewS3sRVVh04G9TJ7krz(qrD$s++rzXF1|M3UE`lDmt|M7|B zeno0<>RDM61!h%5VA-KP)mSN2C)@DEE7Qq)c4l{ z+W6c{hc9``g~J;kw{@UDbcU=Uutb&!4I-M;s-X(1pr8sxLI9Wn1OT0WJBxZ2WzVcE zfQL>t8n@qb?BRd7{ejz$xAB0mHZ_+72pS;VM8qbbHU%{y(IjU`fm@m8I4@mVHe2WeX|Y3M-~K+7ywRZ{uQ-xnhaCwO+(Z0mMp5nO38u2fDMI~Ohkra04k}+ ziW*Z<(H;e;5OQD4>AA1F=Hkn)d-C?JrO-KNhzy}2LJNSXpdtz^qFxoa7}WEo1Q)3* z5CRew{}CZLL?i%JQDtUj&mzs#`_JG0z=I#V^`37xO?aPpZ9~p2L7YO=1_(4jwIwEL zlbFPKO|_lm6mgbkWj`4nlHNm`d1%*=?Ar)+=x%btGW*1>8RIcEYwvI?pqz@q9!)QbY2$u`joUvSx`pLx|aJI~$JUoHpEgoeyPjG+z*a?LtS*3TRuhLNd=uGf;xycK(XaP6CzH*L*`w*V;7{ndEioh zq(N|20k9bJOA~#j5x1?aR$VMMWGf+(Eg_ha2vt$F4+we%ObvW%%hl%%Ui$0LyYzzX z!@a$+vmqI@ERg4{Z5H**vh0jDac*XY&;{0%h`=yQ*2;W#&*6#sAOHQgzcZ5H_eF3_ zMJ5$_(hyEENF5lO2Be+)G}T-(PbTk8oBWdi!C3>q!pz@7rHOr}5pT5=HiELP1es`T ztSf=BWd!K~)IL#DL)8IEn&Erj;qQ95!FexAwF+qxumFBx)cbywOv+2 zXAuB^ONsqhY?xV%387@vRRy32fNF}^53K7WP0qjJrBA$U_a&P)MxiUSXqM(u7wx!B zvuJ(`7HHF&n%Vus&Qcg!Dyyp2TUp~1d+&bmqkHeY!^D;Q^CW8k;zfP1Aoht;KLPdw`;D6_xbt1Fc;=-$xAYGr5{M*4FVW64g?j5JOqx^Yehq39h z>OpHH)UNunphIT0DWuC(*!3AetbbHB_M~7Te&bGDv%}W~<2qLB=x?!?R=$ zoCN^P75yi(z=R~$_Ew#!35~R445Km;R=vlX0u3O*kS5IwUvl;K$NlE>9=E-(9QNd* z?N%m}^YvdB^<7W{C|E-0MRQ#K_O9~m=1FVNl_T2^3UYX?G5Omc+57#6nsSHtIEtbt z5a1*lonY1}QEghy6N_;wC+m5uZ;*S_gQvpAd-mv=1^_L8OZQZWet_A`?-?`{gpoz@ zP9Y0SSc(;?%uGG1*pGsPnzqls?n&Er{q_qkJHNLaR+|}**ApINQ5IF1n?OVsOIoDU zo?%rH0jb7r&=Q6$Dl96jAgr^VTPn?~N2e|d3;))6F3TmIXynIOlsENP`rh-Zt1qab zzsxusP=G-Os0mXK5Lb+7Nx_Da)PqJG+1~V^nG92!Uf>F#3oLhE8=Wx#%!vLQZou86 z`!N}>VHRZ6m}*;8b_zDMfr(Y1OsYMg+7Cbj?9**mo>#lzO)tFs{8AK@>j|Goa;=L3 z&q<=Ek}j5DfutfL!omQc)K?BRID?TWh!+1?6&6TKl`|`evIv}(=HA01`9w22mgd~t zw!U}$|M-=w&JVNJPPC>EfCd1ikJMI?Y{?o3!BJqryUDO@61#y$hk#(;&O)-DVTa(1 zN`Pt6U%y}&C*hE(dP{+eEXI+u21E3=qyQBb>QgoS!1<_nf*^;k))yvj?U?k2rQ!NJ%d7_qH2|J*cmACq9eN)-Sf&4SfE}Dl@GFzbeL-A zV>wImxNSqjH@xi07g5$c-#YA5(E$PMRrHk{r6f@pm=e3tCbl;PYL>Us6}9mK1aJeM zNhxqf05CK9_wB>oyM}NwOsRkCAO>M%(NVyRp%;pkpu&K?AkeSO>#N4^c;_pwx@hA- zxv!bSq6p(I8Y6T1B<0jCX0&j`D$R{`sqSRRP;18 zHPOgU2!K3&=cbLn_rl9Ak*u{1$@YSmei7RXLKR{vDMW!W{niCdI}l`)jMs4Y=>E?B zhcl-N&YT;#=LUp**Td+(QR=OQW;|_Mq}WAaY|Ib z)CSrqPc~AW=&qoZ*@UVJEQ*B$IJFKC5pl35+H_(vIo!^9s#qB?yH8gjYFA*FDFBSs zv!esmVB@tnpD3Y2-|+!@M&THG?Bpcih!AroCNtgx?!05`0# zf{W9dCBUiCUl@MPa+4BU!@xz>#F671La!7nLRGx<0!U4g=J{{F?y`%ox@dSqnoDyk z(UV<`deQkt-||vIMOBz(9-|Ne2YaJUp`&VN7%y+&Ehrhflvo{^z%2e`Jv|aS6a31v zE;?t|hU&#+@*x1|M}}Tyta#yAGzc778-$VVj|OqZAlw6Rrfh*TV*~8D0ik2~4FnlQ z9*nilqOs1RMo3ap)hZ~|eBw7hW7qogu6xpXn-suLwB-1V2Q)!kcUy#`#3yS2q z3}O)#7Fp1NLIhmb8*h#R>XE`II4zH*J&e=rjk)(O;Ys9PvazXjq#6gMH@)Dp4UnZ< z6{t@X`arZwVoIE8EG-S%HnpujGKux448k*H3#>_fpG6|NYBNKr$h zv5P&Lelv*ahJHMgQef>iKu7l{6-ddU|}gzufoAXI?Z+nhhzK9%ZPa zYMH%>G}X`NhV^yvwQjezTmY4k8g&Q{wRLT?0?6?>33+KGR+}>)ZbQM5Vu?Pf-bUREo4wSd4 zHguwyA1`8?%LI>?loQKI3x#?+pQ^^L_u40%JN(5v9vVL|mXEQ=UJz+0+7=paL+)MM zrXHlhetlK02tvGL-&b)(F31A3HVLq&*rA(l=*NDOV9drCN-cOBuxgCRB@n2BK;On% zu<5!do;&1O(y2to7at*oG~m-l%FE)0n7!^AVAe43W3 z*Qwu6OeM$qDq;E8e)f`$D(N}^?E$3<3YQF0gqDLas+h`9m8RnY0M`TTS(`Dq1{**D z_PrSE7d#t%J-`-_ff5;EvW_S)tdUnNBSMAS?QOsO^N$}M>W$-L6X`>&xTHQ_`E1_e zBT`k7I}BOrXxTJaIZpxLA0k;8ZrNn~|LVLo4yEMmwK;!ujiJ7jCF-x_cx& zDJm17SO=B{V$i~7+a*Z3vEHW}D!BUxx8vS5T7s(rz>N5Z>meE6j{~(~Vgrt^SVC6he)#H=N z5d_p?7r|0>>c(zdIExdL?W0@PRkuHV=f>df!#^HS4NnqsSr{X75*Q*Acr`88OUco@ zsW1lDrUure0;a`(Vm;8GJhU_#H#Fo>E{NiJx57n3|Tj9r0Mq z+RaKbKP{6Na}j*$+6FkCHV;*|5A{SFSwxu^2p!L<_ri6YmO@2ZsNNz;&S^XKji>M2 zG=RWW1g(f*nLtV^5@sQ}JVF!uDYL$IKF+9tHN*r~uLAY})Q=3KuLlTuouD5Ko77q{ zPDw*#i~*!9dA|N>yEc_95l*#vQUn%egwFf2JaqwlmBI*IfepAIZCO{{4xn&kto<+o z3gT&spc_MVk#!2Qsou&aH}seL&fnPA^W($qUR4+qz!*S6^col&+nj_1+n8=Rio1U> zhI{vb&YY37LRuvN6ky-Yn81D<-nj*Y!hlKE!p zHJN^Vu?|?a+!e0gP*L)ojT~!TO0i{K&vsSNLnoRKE*>Y)1;5U#*CJmgn(4$~Z@m7h zi-x0nM(!yC1hI-ns3ycfEv2~$7)`65k#|+BUl1a|$g114VgQ`d|2qL-Cp1qasFeWo z5KYido%$e9)ld~nAkuTmdHsF0a%iX8`EeGVwV-pUj+#%p#3(xb&N){_AfRIeE=~^} zJ`U>!`XG*iC38P#ea{X6g@=we9>BsdoKvv{RQ)oQo(t+Uf}8(?%lrW=kTS0l{HlTFonau!enP>fJDf-GD2 zq>DF3p*1+s^y3TaN?uN1O2?(@#%7p*AJ5nTx}^9xIZ@ZoedRW3wbNyre$M*d9p|j8 zoX5qc!TEaqRHf(CX=AUel$Gp!vqs z{Ek}~97`Ep(lG#+lK=?FDPZ@p>{*3SIEz3)Ytid`VR!017=xkY{XN2qq^_T&)*&z|4}dmD4H!P)z>76#%|D zj`@VAoIIHvEP1p5`}fn!Nf+_Mv^CK?%P*xUl)OL zRY4+P4N;9FLhHqmCnldn=A6&ifAK9;y+{!McNL4wnaSgJH4%54x%lCYU-CHc)?{ASND{@hcd zY9(HBbZys*XFy5<6asLFUL64$V?BjI zA2!la^5ab`PD5RtA-IM};7-Wf38*m`#6T>@s6itvP6a|0jR$%n0}7Jnl86W|88w^r zvB2+W2#x1rI~d~pJ9S~}3si(Uqiad@ArU(pOeun@Vpear^rnCLhQ9duPmO!4rLI8m z!viONh=2kM)US&+&bcMaIg6Z$hWg6E$hbtR7yzPU5{qIDPi3Yj3-EXiW}TS2azk)M z0GRCoq>ZB(ffs^0&UZrru7K#4N{+~NbidmMFK&l{UOxrn|0$?U0(rCB!AHMK)ed!CXF7@@4 zy8!`y|GwjQA(8^>B8^=L8}mJg%yU+b0~+irxkD!=ON8hIz*-UOQ4IzGvT5Tmo6b4N z-5!KJmsTYKc8~7ILzngg@DOm;M&m3B0vaGAs8+xjMU8q(0f?%!GfC&VfTz=*h^l>=Z80PF?u^(q>wqt;?XK(OM7MT{s|1aN`Ds1!M6 zR-bwCr#E#ToyI`bLVRx;j5nUMuDWCT@1@A~T(xWCH9vZI?5-aloVX8(WPyxMU9kYO zh+ifYs0zxWtN^(&K^BC->_u2q8|{{U;xo7EFJJfc@<6TH1qf7c-+$zLh$Mw)wg|xI zN$gVqNV7asRjI@QA%O)&tHf4CET|H)LFlOfr=ysjm_TTct;!Qv(_8?jO_a={WQN28 zDFTs-QBq zEE<|yS1WCP?iE{}Khzrzc^37|;$^ynr-3wm%}aU#s)fwr`_b7NgA2&2g$j8C{A zJkd(>@sHm8Z_Q&TCb|K^_NQL5`3brA$O|%iAz{;0R*@-## z%()lNc~txY#iKS1M_Wlg_K};u(ik~DzAOmN+0eKBX_uYzq%8MX z)T_ul=c3)-clu?4)J}cIq8u8_Kw=csr~oMgTCu1u#p>w=?pdxz^J(P(n0fsQKyTVa z$rDsIXc15aP*Dj0C~#yAp=7z_o<#~q6eDq3EG{teCHJgpp3!YTe0b#EhmW7^rb>v2 z7o9V3`SYH*^|`U5Qtny3=ToD<0Kt+=f~43%bZTT4>WURGC+k`+h$f<_H0r`|w4M3! z5Bx z*kj%F(?YcRIKbx30L^jGTu}uOj0HkZph_Z!6aZO7MM1om1&(uBz+zWGRiQld*r?a> z6Sw^5k;~5C7+ibJmDRv4U)90h(uQlV+J5af_Z|NFy@w|sMkvm8n+Drb&#EX)15;6i zu<%+`6cs?oIB{Xbp=b3u!xJvqS{>+H+AEkQXQ(3Vor1{`XDupOhE!EN3n&7q0uc(4 zS_DAgEGI=p7zBXh0QLInfU{?QfHLbhQ~*J=YF&ueJ^P}I9)IzcS~val{ktEU_`8-ZQ=Qw`!5;lEp7PXZTEle z$njR{y^38nzy$375@}g%3r8ru)Mt#+2@cL(7{0p~! z@4)SQZyT#Gb+3qO;ZBMu3e$9aNfZ@Sqrk+!_{^O@x2{$hUanjbg&RMAYxb=>@0kpv zxTS!J2Fw&RNy~1^|=_TZ{8EvOV~W06^IzAfXoLZzB+aB7g{p zut+{z1$41ULIniKi4DOA0GR@&fYcU%^v=5<&JVo*AFHo>$#dch&fU0dA6UvCc|!mI zAOJ~3K~zKTnkQ_(YJDv_LbQ0FiGq3-^`1omg^*NLPyw`74SQewtX(hYtwhU;{WQzr zLpOdY*?<4x2^WMXtP4k75R9WSEd|Uyi*{BMElEU(tSVkupjdu}AczWrXYyEwvvNup z6q_-$kN_&)FXe+T8ZBi20E_*-x-b@O6eR z*|BNk(p4@bK<-5ZMX1=HisuaWtb5V3cD|q#1(oH>m~6E4&p!5r_QNA%6JZpNy0COS z2;&oEqPikwo|TKLE^h)@LsU#*tZmu%vgMr3%smV1j1joRf*f7gLg<{3C}^yAL6oav zssIT9Bx#ytpZ)S}*}i)p?)ivQ3HrFW{_ESz)1eq_x)YZ>tFKR(iN9(U$t!P*Z`V) zuc8W1yX>3`pYx<0&mp2^t*m<qLSx}p?HzDojx%H0L5Y8#OMNV6w^R4Rlt@&D`~cq^gVxl zOYifZc3Jh8uG{ULUAbi}s*roHHuvy?t1r6ps>`-s-OY2ifB#0}L2CoUy0e{ZWa<`0TCuEnmHT+}PlRv(XV7mXDjDG!7s@|p&M&|G4gDbS=blrD9-F@ zZnNlYssLpv&g_^6fCbdrTs$l=Pk!2EVIV34SvD@toM0*nZ2@hiS(?88Z~i&EbKe6! zzxj%v3rbPAY<;&34^#kPSxetD!~6f{pVM2v`=c>u!{f$9BPNQE8yAfO+JsfTc+hUG z{l#c{YWgv21d1wnFAO3b8({vjr#ZB+-d~~w?JTH3Rx}=LnA-pn0c?ptn+21v-L^0L z(S3({-ummmP(FX#@-c(usM${8Pygamt^fPugJZ@;$H+w^CWw!rjV2XMlB+U$(KB&^ zTQR-8>{7-x)7i8EAZJlz@tOChCW97Y^xQw4x|sFjlHH&n0LWlmPC=ZJLSmHKqS`)k zd}8QLZ~s{S>g%7?^P-=9idijlPK?#{5C7MV%?FR1JZXdADB0*RxoDJZG^t=RFX&W01Dy7ybJ=}IYB6i#0fHQ@X5cVE!KR9~ zvOG)w^2UGlPrq{4;JU%y6(iulp=0{i_k40{Y^r^NY&e1@I)W}76&p_F3MqOJ#3dFz zuY&TNB`?-AoXKqG(OCmPma~qWY&0PEQzs{y>FFbU?&TTmI|WqaL_M~|n+AK^6rVMH z#+epNSXS&K-v7|C{P<*WSAhJs^}#Dl}3B6j+0$zZ`w*j{8PSo7O!JDCj*~Wt=tlVuq$)&-h3&9{>PR zMO76s7Ydq+!MF>e$-uc)XSr#8t^CP%z4p5IzWKV#6>P3uPU$|{>FfjGCq;;gimCuI z&|jfjKKRCG)_TjUM*sj=L-rNVd;E3(@uAnee*17wkE$Z5p0yy&BSAmC08m9m#Y@i{ z?ECVc|Jtuzdj6&@XDaK9whdkSl@Gq^_h0+M%P-X8{YZ~l5d3rkKou1QpL6ATn?CcN zSN!hAp`InDf&l==CmZnQcYIXuJ#eI}BC4gZ@7-^B?i)YxN7r8K2rTH?3)h}a`so9J zDk>tr=6RQ2__05{?svLb!~p>C${yl;K< zSKoBSh3f|tgdS_s;HL`!#oP=1!7HEggm=E-xo?O=SL*grV{9AH9BZfk_*>t5-v}WR9yI2 zQ3+?}l`+ODkWs{9VK=P&P|mfH0veATZhkxO6Yjd#d>>HJi|e!GbjOa z00ua-FKu6jR1pxcr)0t}zyFtCyZegscCR)oL&gibNin6PO!0$k<9nIh{h7d_cYWxy z$6xi9541+drn<^&!$5WOC*Jm=x4!$=ueqvN%zuVn^2Eh|NTO)m83DqY0APZ$QpO69 z6?+qq3h|G4_BBAq&D%Ou>G|4Uy!y8vw`2J6Ys^s;AyY6dw(&{E(m^q$`vk*#743t! ze)sO9&wtI^C%*oj{avYqz?txMKY!&bzVg9WzIOAva(UG%0Xrd7)Z)AWhN7?21jvSK z2EnQUK;@bOj2CtER*{Sr5EUq<>d#{6ufzN&Uc6!Gi+}dY-`zS~J8!v1TJ02C$r7i3 zVR7_~ATgj#yon~nL?djY2biLJ1>Aw-#jO~+o#^~(%&1ZmDex#$mrM85}D!Ka@ z=+OWE`!64Rb7|&CmOBq{P2Mkg;x_Vn3)?FLFJAJm{9>IieIbMkyZ8e$|r+% z?NaE(N+!dyMieUeT=QHMiHg@iU?Bsri{;cSqGF2{z2O(G-1P^qe&*`}XO>;+^T5ML z^;K{AK>Oj56CMGpu!wS+**c8HE*u0B1yBTNGnG7=JX58%VN5C&=ilo*hSw3Ap#;fKv0lkK_n_DvVqj3P)^#AjMKXR5B)_#ZO3C8FhU8cl_EjpZdD%u6&v4V!8Gm zKfF&~_uv0=VtlGK1!$Ov%CM5zKm=7F7Bqqu1R4Bbe~F{DN*C`PZ-C$-n*fkDf>} zJ+5R=ssbV6Ij{%-mUyL@7O#phe)Djz`Rsds=@&Pz?;BXI{JppRkYE1?A3mOVs0#&6 zFg8U10qAaxXJCX(l#{k1kb2F9z_J3cqB0O= z3S8=PEBt&?MIeO1o*@40|9JW9F4{J>*jfLax5DF`DYqah{)M#G>}Q(&^HhLuR6vSC#ny($T?cW9@=eE={z z3MPn^a#cddLc*%xQN^QT3V?CZm=#i|<1gA=t9|u@uX=Mg%+GQUZ+iR3+8@5@t0M~b zVWIGTq3A(|@HnGu06>1~X!q{xaM|{C1D|=<1UO!GxKn;)O96W%a7)0U!s$1OyBkvMW-(gg^@B!cDb} zJ>{@(x!aE%AJZ4V@qLru{N8;>6y1YD;r&eE!$R>%#h|TViq0=8v8XUofI{)A0)f(# zcWzvN)7!6qb2rSt>7V~I|GjsA;;2`1STH=`DLN=7J~6}mRp>zk1+*qq5e30iLA)w^ zqvQ?d?5c*Rf=p?=S>(NI*QzGqRtbQcA*v1QvGJx(wKBD0#N+{!GUNcJB`n62 zOZ|2Q06-A}@ZrZl_l=Y9{K&t2XDN;Oqx%lYi+=OH<99!B?66R9Kqz{EO>~ek8WXSu z^HIa8xb%E#e@%tiyvkRM-^RbwafC3gxC0Z8Y8XL1&E}3R?s&{nbByW%B(W|K|mdiu=YLKa%U;_`VZIPD~ya zatGLi_p>b>Vu~h!T%t$}2ug%#h;aH?r6pRW#*(?_XU^OBw|{*78+$9!veM^cH~(Aq zhktg{h@v|rCOjY%9b^no%t)S9GT&ekE~GG6r;Hv1a|Osm#S5b+NyUg28znju1lqHj zlGgCXkUOd)YO8g%jjATn7K6;9;0W`0;OctA|_fVc68QZ%6^OpP@fI15o=DRc${XV6)* zmZcJHvY^Bs^yX+4^P^BOf45rkE$Cc>VAHDyk!(aK&yK(@4zr68F*_;0KZ;ob+ zhtvcQh=~q>Egv&BuG`T1*cl81hhbn%;0y)MlCy>i04!ead7Ibue)x}m=|z=NxaLB;R@Q+!Y;JSiBo*2;VU01E=PQXUqsplGH9Bmu<~5Ht3fFMvJLSlrd--+8?A{}-5eT+k-6-XI|vQiDi{ z2#A0LN#?asYn8>2>>KuAax3!#0lqCP6Eh4ZCGLBeubM?7js|b&L9NXwMU~P!#XnF zRGZgDqd}%1%`w3YklM)D7Lqn1O#;F>1hAi=h_x)8HL5BI0A*t02~{<~imfcmZ~6DT zjw1yPwDAa;a0G&Q+_<;}2H60iF$6>f_Yo0@5K)Dc3@V#72PNP4SGOb|{q(;bH8wa* zCU{6NI*b&LBbt;L#jC&?-%kk^6ew^Z67_J7P4JumN*hQMCCoI6vRv9eER_Vzm{xx< zrm$>ma;frGOM3vSyGL~L(tZuu2qUraF3&K{5g=ir7BIGu?3hY+E=6J4wzFoYU1%Ld zRj@3G2~lZ`0fB(pgb*XS1cTtDweb|%Fd<{b5Fil&5dtA%2LynKh>E0WNZjfHpsMiR zk9;oqyDxnGsBzI@Fwuix)$#_(#HhL{RcRAQ zOAED{GbbEKA`z@p7~Pa-OS}hkcUXmv?($3aY9St95NsJc$&wje&rS%Z&*VGB!8Him`KrpZemrk6ITURueo7wmgD1nqV-g2&yrHEJ~hGSx60uvWVt>W#WJk zfIxwwaJ?`kD$)X#mZ)hP%Pq&68Bm{Ad;R3mdv$%StT6sn0Qi)uzU>iO4*;|6fEQy_ zd$yj;0S`n%ot)cY8;YS z8we7CH3)`~rkQOFAr%9E8WaGEC}0RpSANJG5>ca3a+V~-G(Foai zQpn}RpdtcB5t$VYp|Ysvh)RM=h?=u#w=b=ADBvok8X7amZJbh;mI^e;a0|dCig}g? z;6t7i*l>|le{j3P?p>?a{{U->1Qzjt>OF-!SPF$*OF1->p-L(wEd*@=a}$g^%!%{{ zgSGPmSoqCqr<1dUfhBZ-0qd+<>o~HJcR?i95{8b@IYYLv@F{SH0!tKhzB^0gifiLK zZs1A+Wh?^k`14P-|K--Zj@lqP97dIcL8*Eyj7w8N6lRW$1ci&)@iGXVHRvp1*eTCB zgMl?@x&R-6f&&Ft>)k0ih%iioNE20BMAQPzGc5&qXpkdsH9Xo_llwPKYug^a2Vn2@ zg&G*!yg`Gg?G1(=T_Yu?mIyQeqz)_%MLPyYcACf?a0w^bB2oYm!5KnpiL4=XhR_&7 z+Zn{Bb8QIGbPPUY2oO(QBNeA>BO;(Gk`h7T1at)fW`TG7`KMZ6{r0}&K^PsupmfLv z(NT2qB!I~jNDV29D6*iEs7{Z5W>k-&M5vyNcMB7tdPK=t7PSpRQgD4e!+_YwDbYMPd@mm*4Mv#_wgW%kC-4jz@dq*ro+UN~C7_6NPin@FySm z$JV#LfB*3?ERC2jJ{&~lW9XtfqRERB+W}P-NebQ0sEUY6B0z-2Tqq$CqfWnF98VpV zO^7hhE(lN|80g!otYx2?N#(qu08m==0XBO`gxgT577ss3O@VfTo_&4nah3;WgtfI3_43}KFzzck=&8jrvD+kaaA=YM^AlIF#;BrQHm zit^and_in{U*}Gu*l0eps)&MxC`OUZd>HAET}MRwhjf`qx{wQt5Tp=Iu3q`Jc(_pW znFK#qktu7{=H4B!vxYktVa~s6 z4Lf7bzhju~`gQj?Im>L!t9t>fy20JQ`N!qa&p$iPi}LewF#Nn6?i}a&V3DRtaMr+C zL+fig)Ac-hf_8zbUH3Z7c3b=QF*$qg&ej&rSwjP-lGlVb!=|oMO zv7;qrc9lu)2P=duSUEU`;P!p}|K|Q*mfzY1T(AO1x8ycJSu*COir0YFBvf&Ta|t{J zp;RxqIQ!5q{I~OKSN}{;PXAp%5if&pm@HF&-f4X24v3gKm%v*wfZ0j_^{0IdpbY7* z_j22-RfE6$!6(ZH51$`rMe!`n%FkU^p13rvm~HImaFA$M=PsfutYA?RB8}X+fEcwm z?@9y+h(J-gEJUZG>r!Cp?-^$l1xdL>yTd=K7ZO85WA$Mos$-B&Ec4vhSViM}Xj{K% z=X$*<g~R0f zl|L;Unbgt+@ZILTdw*Mt&I84b#;sk&Mg#k6ZENa<-@05i_=~^$Wbuo~FHZ8Jd{zvG zPs`!xtSpNvP14}3?KPkI6_2hBeL&j1HtY8{ZQ2fKjXAdjg(b?eBulUEeJCv`)I1jN zXA*S`jblqRiJ%2@Ru5d;?vxUDa5D}cJk_=KUlweavt`Vd6}WeFj1TuxdH(E#vt$@d zVroj*l2;d+m=R(oiWHt*V3$c7-%qaW&C~h(ff(wdeI_=W8%KTgzhwb7THp5o_(Bd~ zt0zE242E<-tWCh8s`ama@XyOfPft$rym+3L<K^Cbht13jHs;biU z{Z&X+RF#14NU9{=Ya&QLAlPaA`5k5e!cb-=jr)6l(lqMo1x>}bGZj69hA~(tYIw#r zR7k6)ZFa+4YPqsE)_XYD{>w1`>qcFk+`Xf?eP4$Up6U*6#;TO)wecivBhV!hnh@lS zB%W$$$G(*p%Oy<&=9V1|ZkCqFZJT|wrD5hg_&e7;S`&oLrnkUbT1&kp=wkp77Ry@x z`iK8A`}Fzbq{s))%Hi|=m zrStzpg$42c?rjm>nU8hp+c4G;&k*GlN~f0W#HgvAS`AB{|-n&%azm`i| zC~{*3?x@~7`b0n68*9=gGP;`k_JuaqC^b!M*cvASi>M9=#>1KUBz!Bqmi{p_`!a^H zFcELvaKmdePh_~e`Rgv=b{w#k3m_s8bGKZJiXmqW7OUF-;P3x+^7-+!D$3$G%ZulE zJ~(q}x?b`j3W(6TwiB^LQG#lOD8SpZei#q|7-~Arx1>M>wdL>w zkhw8C4Rhc4Gxc$-K~KT)gb8D1ni+O0Z}a8~7GWCZUEuf8eeK-rWzDtg|!+-de+HF>=eW;iu%@%2%Ptzo=$eKV(>$36A*=v79w{wSf z1n8_mXA!!*zo>S*qxJg^cSnuu?P^7oV-!(!i!}Rg7401CxPL_t)? zb6eo|E`ZlnzgrnW+8PV!^DjmTdF`y3K!GGlRxU{zHb%O_v97zq?F;qM_tD6HGvPjrp2PqUBPX=cXSIMzB86-+F-AF3#WKm`&5ub zrX~gw6(V7%s76((VCB(gXx_bn8+Jc0M(Tg!peyAL(U-xu8<)ULTP~m@0YCs-cIud0 zNt`f~8aCY=yH|nVpR>|h^Fab)B8?)_5eh=Yh@^~eH!#`u{fusE|H`HZ4VmxECvE%i zQ8o2-GZr7m65=Uoc)^Tgvi3}YxiR%h^Rl^C4#VsT;=%EKx%UwSckU?ez8>&_*Sc>Y z0e4k#_hYQ%foWN9O()c4F`@nOmO%Ynw{ z#6&Q?sow^F?Gk9?3eLv_x`Zs?SV$1MiV>D|ejfc0p=$S{Q+(>5NJ&+hRHG<}n4_o? zlnJU^8s9$=96*uS%#C}uRvbQkwpu)SvYdzyQ}tn@A)GMin3#{r(3y(POd^%0MYE4( zI1XoW^Y(Y;-cj%O>9^1K|5}t@=Vj z=n9mtlHn_Cc@If4diz@P?z?Xd^0IXE0m4dz`9IL`{j*PRrI_UpxypQo1=(I?BjuMchs(rAW>)zKCA?9)pgXLKU&OQyjYFJ z`CmI{p3yj9w=JfG;OOF}Z0b z@mWG_GB*!}2MwUK8-iSAF;~>!Dva4R){WBK?Y@6AzxMX^GAnXB8IeB92@CBjK@Br+ z2Y!7Vu+9siJ3-Hdb(=2A`X+AzK<$z|qS|lcz7G0yLjn;|*6w7Xe03qXS+~(Xep*c* zKAz9pRh%QVGYN4F#VKh#fib6QFf|$G#@UL7QV&)V-URr-S3m&Z*O9=PnaLbAZ5n-B zSf78*3^Mp@#jXq9@8ZtfnYtT2DK_+5s z+`*aw>$MkNYUZzx{?CuyIBxflN-&y5dZ!MOe)@d5c<{^VtX{^2dcQ!FSqw4;$r(sa zEzwxnPEFz$23Vdwl?;`5Vg_R|N``Et=Nw2f5^aiK9z5oMx~ zF^r6jwPQ<|8OuwP`&u|_>ud9{^W`t< znG4Gzv^psY|6;m0J6kO^#7d(q zHN?59OjTt9(g`z7g?P%=FOAz>M#IhMplPeBOYt7uMi=nC0q}vZm;k`5NN}Hyk8bg@ zTyn95smf@v44F1GbAztwp@^43YE(0N4rxd+kRQHD1*Qf%T%cjgr+7Loebci+1(oMYVYN z>AZS=Tvg&@BT;LOVTmA1MVP5z#!M51%vfp0HZDxTOL#PNXzIa=Lbdx|+-W|Q;e)4o z@1wpzr%BHTckbL-mmPfN-~(@n0KgUr6u`$9T|#FKqNz$)I$NZq zE`g)0UTFbH$&rR4I)w5-SxW{NFqDH#MuswGPQa898%u@^JGMJTl8**SG8#BDDia?KQ znCAppQie*6))~20tu})!hM5^ixqKuC$LMnVUjy)gH%I_ryF0e!5{~cF+8QjIt7a5l z@N({)v$>;1lQbdbV9m6RCCC&SP=d4oQ2{a;Ddk|KAY??85V0j@s|3?m2beCer?j02 z=l~!gg$PtsB$6Vi#Grx}kQ#ypsw>rg(hGA1jTMwCa#F$43eK!DN?M;=Kk(vbfb3ut zv%RsN59#8Knf#aKjS^tfh*w&J`*eH+ZVvZp*<3Z{^2F3FtZT?IN7qD?k=PW{WI-s0 za7L;b1UW#NfHQ)mMC_Ew5y${)iHH?yK${F6pe$Wx52A_)XwWY0+p54Tv;m}MLIdFy zh*wHdgRyeVwUTd?T%Bgl`y4)Bm@t#Etj+!K!BgF6{oQpn&zlDyc&(}6-&{5WNPSWk zcR$8GK!IEO(Uk)_KKhY1hx;)$S7RAgepQpr{i12w)-hWrttJeUgn)#|XA~p@Q9=wS z;u8w&6l@4;g}@+K)ksk3OaQTxK*0zKiUbV=M1gu1@yhT9+^ABmi8NMYt7+Oe;WpYj zWG#E28gHYC)od)u2T6RZ8Orb{J@6krRowahOXmKKV-MhKVga^gV-0{!ka!*+{HU`C zziQ3FUW!VFW>uZK47nwaQ-dnaYO`KmL#w=+cHVhO> z?Fg`zFT@7`wqt~PyhqD&$<4_r@8zD_~q8$p1zT(AmXN`|Kd+`^g=aWx^}0J1m3m6|Cw836$R5dD$>z__x%2^J=;^)wUZ z@e(G#ssNBJDoF>2n(d7h0KN!1u-10q^$y=&wtS-qur0470{|S|>XJ*x_Xz;z-?>2m z&=CUwZUF$A#j^_*Al)psd>`4~jm=G2FT!{MZ~)ty4t%pf-8*ilhGX z-*heAPrAbfY~Xw{#yM}ksi1Gm?*su}C0`u#fBAQQy!H93X4+i*^WO!qUnTzwWGCPi To8JSQ00000NkvXXu0mjfGb3Pv literal 0 HcmV?d00001 diff --git a/data/themes/images/bluetooth-inactive.png b/data/themes/images/bluetooth-inactive.png new file mode 100644 index 0000000000000000000000000000000000000000..4844f97f13538234039793248b13ea0365b2dae3 GIT binary patch literal 11987 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 literal 0 HcmV?d00001 diff --git a/data/themes/images/bluetooth-powered.png b/data/themes/images/bluetooth-powered.png new file mode 100644 index 0000000000000000000000000000000000000000..57f557644a3279546c085eeeeca0ab35b171fbe9 GIT binary patch literal 17744 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 b_I>&vtTq)C|Ho(d00000NkvXXu0mjfpb|5o literal 0 HcmV?d00001 diff --git a/src/modules/Makefile.am b/src/modules/Makefile.am index 32c8d6718..4b8553d65 100644 --- a/src/modules/Makefile.am +++ b/src/modules/Makefile.am @@ -238,6 +238,10 @@ if USE_MODULE_CONNMAN SUBDIRS += connman endif +if USE_MODULE_BLUEZ +SUBDIRS += bluez +endif + if USE_MODULE_ILLUME SUBDIRS += illume endif @@ -323,6 +327,7 @@ conf_scale \ gadman \ mixer \ connman \ +bluez \ illume \ illume2 \ illume-bluetooth \ diff --git a/src/modules/bluez/Makefile.am b/src/modules/bluez/Makefile.am new file mode 100644 index 000000000..87ba2ca02 --- /dev/null +++ b/src/modules/bluez/Makefile.am @@ -0,0 +1,33 @@ +MAINTAINERCLEANFILES = Makefile.in +MODULE = bluez + +# data files for the module +filesdir = $(libdir)/enlightenment/modules/$(MODULE) +files_DATA = \ +e-module-$(MODULE).edj module.desktop + +EXTRA_DIST = $(files_DATA) + +# the module .so file +INCLUDES = -I. \ + -I$(top_srcdir) \ + -I$(top_srcdir)/src/modules/$(MODULE) \ + -I$(top_srcdir)/src/bin \ + -I$(top_srcdir)/src/lib \ + -I$(top_srcdir)/src/modules \ + @e_cflags@ @EBLUEZ_CFLAGS@ + +pkgdir = $(libdir)/enlightenment/modules/$(MODULE)/$(MODULE_ARCH) + +pkg_LTLIBRARIES = module.la + +module_la_SOURCES = e_mod_main.h \ + e_mod_main.c \ + e_mod_config.c + +module_la_LIBADD = @e_libs@ @dlopen_libs@ @EBLUEZ_LIBS@ +module_la_LDFLAGS = -module -avoid-version +module_la_DEPENDENCIES = $(top_builddir)/config.h + +uninstall: + rm -rf $(DESTDIR)$(libdir)/enlightenment/modules/$(MODULE) diff --git a/src/modules/bluez/e-module-bluez.edj b/src/modules/bluez/e-module-bluez.edj new file mode 100644 index 0000000000000000000000000000000000000000..bf9240b0cf838036601b1f58ad276dfbb5a18e4e GIT binary patch literal 22035 zcmZ^~1z1$i_rSe~0*ZiufJ!P-(ji@nf=a7MH%l%o-6blagh)4tQqtWa(xuXkbgwin zEc?y^e*gS@-}gSxaL)PMGjnIooH>_RXcma>Lm-d~prt|}WF`;@)-D8sI|qRfNCHL( z8WsdXp$&nE$(%F-1;8$X2CnCyz?aJ>jX)L1tDu4Fc|Q2E54tvjaV&sN02&xaB?Cx| zeF%^f;kbq1rqZt%2Q?lSQxM|eqyJr1h6<@W9IShl*s{> z4(ux!8*>3Y60kBLaZboF18X9v0SwcZ=hP1gSR1f0bAEWrG=Vt)Z0s|Ap@0noMhV)- zQ$HKPwt5O6_|Oaele%)9$?jgo!yVy zfPKaI9d|tG3+6=#)(jy5Ef6%2N0@aI{sI#JjGrxFtAJr*7<>8*U*L)hY`immRDfaP zbha;u3(*b0{;usc(AfaH3FPH7eP;k;2kh*4AU}!j0(S9?Uld^C|M-EtC6fM!f&3=I zoF`1aB%jV1#F^;DDZ}IpCO3ZrrUT^JSb;bYVfGxemnNruLH-e$|MdeBv-jSBSz{Pz zmZyFafH?yDc)h1g05D8$VB%$W${GN}#NuqM!2S@0{KK38ivVX|1uyk7gz%ks6GF) zHhRD?{#QT?KkWGEo1n01g&-GS)kw zUk3~$&*mi4-fYoCd#>QMg-vq1!$m8dp zGIhX4{$X)|E&bCM6BkUKJRT27%vm`P7-rnF&*+0dh%q^Qb{$Z_$N|G}P~#vUL8}3b z4#>0T56Fs^_6!2S{o{$)U7_7&tKu_TaZ<2()63UfJOE_g1 zNeWsMU>O*O@xfd`2YE|e^bczTtQNyS^F8$g>n8^MfBND92KMQHYBn=qzy4vMz7k{3 z>e+l@01WI2T3|FTmKVn>3BJq5aXtfy$wdLMEts?Tm$3sK>AkPyTy%^8#HAFmPUvzb~N!k_5C*fFUsKWVGY)KrWLW0`@od`aq`!=?HQf zLVo&8Os&xY48;0>`n~}SlOt!(5m?7ne!z}n1*A7n?N zlt2gheANKRzkSVsUIv&gki=)^4D#&iE5OLkFfi8DXuwF%Fc7D!m^h!EpCn+VfSs+U zz~|~$z|O{*9G1u?VK zH-TalsF68T*ADJrYY5dfhFjTN>RX>>b|;3h84P-&85%+i;g;6GwuLxCZSBB#5M!vm zy@M@O7uXO>xRC=4s%vHlx8kt2GJ!y?>}_2jrw_1qv4;NDt@Um7Eg@z`kcXHVJjTpN z_ko!$m?GTPMfc%xH%wq`B%(*M_h$Itrzbpk8< z8>8cKkG-WaOFs@5#7f^13OQZ_)YuqI6fD-k4yp@v1oO0m*g~yA9R6BnCRT6|5@VRY zi5jn}bA}pmIGsvUh^3hourObNuJ4S|!BV{EzK&V#AG%?V$F5@19zx_sop`a-g|@C2(UF07m5b zXV^3LaaSIWv%~&po!vWt5)QXFJ<+YKmDXV~E+&|8&eT0)hNL0F3|1&Hp_A_lC!^H@*Sfnd5Ji{!e!SY3a;it#4#xW@Q4-=vnLJ-pO&s zaE#^%vDXJRfx{GT2{(aSK|$?0C4a|0x&Pl59DD!Y_$PP&+Ag3#SYwWoHFybF|206B zF$HB2+_8mY_CnVX>7MA8pl+MNK)3%}0|dMU!K=gC z7H)mKM^68znx3l1#rVYGq#_@$Sr={vbAiAd^mR{SaAN;|z1~jVTA*ftH?OVXv4ok~ zffvY$p?hp#-ux$;^N4oD!83W~t1V7;EnR_=;i@T0x5Y%mWLO4gY*S~6H=hWUZDu^e zBYSf@MCmFzcJhGo@ae&-UC%1Qy}L8ENTO(0W2I_f*iGS9%oB9S-T6Fj#!xvZkE0l6 zZtt2SidQR7!)(%J;DsL*z91K=?fRvI?lSUsdO@xOcd~gu7>e1#l$e#6`|^VZB3DU; z1fU5^&STMStH`lR((W!B z?Bvr?JDnLzPUs0VIp8HbbsI*DDxw=a$ zeMtOU*_yhX#x?jQWqr`pjG*pC`Y_=X+QZw7c9#q#g1SHaPP%ufzW>Z0?(@@bYAZ2e z$k;NK;o7tF6jwFKIPPfuJRe1t%Uo>Msj{Q>tUUL-l_eXACpLXurnT&^(y!ES2;Wo* zH!F;F6q~5l;NvdR5W;YhGARua4JIp17b-*cas9pg_hriIJOpu2-$fubAwLkL+amr* zMQ+s1Hzt>QROa!+uS>^7F>hTuykQyWE%RgGS~C*oaG;1(B)6TZcvFPg1#ZbbkLZ$d zP|)9E(ke7_@NAyvoXT6=p!9sgoAsyaE0H{}UvXW1T~S?||3bqd0Vng}MZN3iJ5aG} zN7UKg;JZXj!#Zt1{C7EHttH^9k;x3U=*k)x$O$ML%FD@xq(u)IAa1xJYA9A~lD)5D zUBGI>@i%CE`}P7^RYrzdzUpd?s^}zI!fCH??`idImpTg4T^G-u&h_ys5xefr7~Y~P zUi2SDWF*H+uY~hFbS;lJ@<_i)@HGxpcKN&CXnT$N#Ff}szTSap^w5rM*C?Q!h5eC zmri8=+U)rx?=iX%oEg#PN#~OuI-!lXMo<$lU4oRDQprJH3f>!{M)sA|NGk!Z!%ICL;0F4WG0 zwj~?i!^XUavBFlEoXW+KlIkfFP~P~THn*N;2M+GSxr zI4|bRIT5xu&~dIeoOp5R0iVYKRM! zelcGPy;Ms%J5>0!!rx4RvwL+kYuky|)!iOZD(bNu_#nXmZTJ!niQmVq%HNSb@ft*QKGeW z)XAky<%v5)v&^7?f#Q=c(Q=Hqf-7NR#ZDnoviK+s)MDaA-Vrb2{@ER=1Zm zJ9mhccBqM|swvUX`dEdl@=$v8kGx+qJV`Z_KH_)>k#9<>xfDAow=(ufub>9u(&I%5 zXgqhqDT5mBpB0{=etrTRuV9=RyHRUGqh=~ec?l6RZ+Y>0S{rA}-%j$;&=p4%%!BW_qZ{J|FHceB{!)PXUcqjc9625oQe!N#^Uhop? zGcAwtzT%GXq({C@!=j_@PN%ZUf_fQ^6oQC@TG7W)(0E$>K0b$m#KrYrYPUG1qF+|| zPu5uTxl9MQCm1FGSE`@z}gpqS)uxcaAF z@!C7fflTLI!QAK`zh&S|a5AaC+AC7%+8xkP(b0!&U;>PbboUEt* zCQl`8Iz=@-RZ1{cVXc)YDk<`2cWOCPO?|ab-rN&))NFG|RvA4bT zcHaGAVFxyTB!XU#)?@A^q1_GEr=zE-zTPf>Wk??1 zR67m%ift>x&`tGTlk*p&XPt|%bb}r3gfyDi<8>T`f~-+;oc%ugg7P+z23L=_3tAuo zYdNu&bB$&z#~!Ue&^aEz?76qNSlVx{wYyQHnWZx+Ql%R>)>IqA>rYw!%K6;E6~(FD zC6D_wleKJRHi@-xLdCT(P4d;I+JHDbcEJD-Y=J9^Z&#Rh-v*dgxcw=vq+)*3qoc7+f+Ys%%;Bb#RkBRAzI;|ttgTYQ!&xuA&)s(>++daNn z<0k4=R)w2!F%A1c=4Gka*&=>{Ra}HJNrLL%dTiR29Idaqc(SyvE6; zPrTjY*im9w^^PO z1l7_le}otc^d!>l!F8UMeyFUrvsJxiH8ZqED*Z8^_JWv(4b=g7@(0Q(WC3#^g(!)Y zq7sf3)g*&J@S0rY%KjSi$2YA;Zv0?9S}n0cdNTv3RU&XfWY%L~9?PYP$j^8Q!Ff|{VW4qNF`CzP``ytzX)?7nf8|QagV#Ne8wC5%fg3he&sY(Dl{?h-q86X^+N|f!>L2@U;|i`#|d}ca!Jq z8x+JE`9ud(Uu$;){2L3(YgXovOH#{!lvmTZu4`0GS#En&J>!oBVHl^7+>RJm+cWDJ z%H0sVh;+-LT6T9r&#ph(x$IZ^r#Xcnl4^`@km$a2oAp|~Yr*`TU>1WbH_Ay!vBVy> zRxINEDvJG7+LDjmcekgiI3&Ah>eeoq8}W^D@A6wW^tUx0Qr)-co*DWhHjo`kxxiKX z+5|^G5%sx?la}_4`;Dj4vDwT;suk49>*`<%`#R@a9KNU^r)&C&pHctrTrJe{qptSU zO9;63n%iPdgGXz;?!|}cOTnzWIiajL9Q3(ERXDEd^d^ou%7y`tmy%l4a54_w{rF;Y zWd5ui*M@aEAKtKTEtGVA!FqL!L$=!hy_&0H%l`!XK!7vehcjQMI+h~BXL$1W=RiJn zW#jPiFC(B{al^raf?-!jBiom53zj%!F2+BI*Agnw&5ajk>vek`^);#bZdBn< zBk5sf-@K81E=%%`ob|1UhHI17Xe*T&&Lq*>g9_B)9aT{b`S>>0EG!?bwPpdXc^}sY?Z7)_W;-yUui0|h^L*t+Lgb6y@ z+MQ$K_j?;7?~fgcwfoX=fIYl{Y;&?L{FlTyT@}yNGkM=|1}O3lthuu1h^>WxS>S-B zrC~9msK01Ku&+&4go2ghR@$J^+TVR;FkbZ!!O^`3iT@ z%?H7h>LDK1FccMjumRfiHG@pPmr+81nlJIq?`Rt#;u4)zi>t2N(({)+SVSbR{|v6Z z#9yzrH|$7;volK8;j#L?`STsGKH0Iz^WE?mR&rRgxM0*d69K>0Vfl;k_r~OIPva=i zd!7>(zIlhxGAo}#|c_p36Yon!?tq7fm|WROFWS87_Tx0toRBar2 zO{jubt42}yi3( z?cIjqC~HO@wQiW)6yLNlKb_T7pRc;Dc}+kVUPw+H1FmY1&k^F&3)HfEB-Y4As>Lh5 zbHuTNg^Wt}2E-xSIi%j@0gZTMzp!juV^y$hzjw;*Q`dPq z!T|>Ba~q@<*t3hVkbswg=GpH|Ci?6PLC^&RrXll)FsUtKV)|r2UExn873aLhNIBL#8qP@oJV?A^gvC z7k_xoQTy(-9!t|+qMYA;gs84x4P2{!^4xZMf39+qyh24ojS5!<3hfJ5dGjeYq*m_t z?XkV|Sb4m02+#X`5|UvezLBGIk1W)yI|h5JJSx&ncCA8re^AcO#%AIi&dr*WGF@S8 z+OYO2m{;4ss+@h_v`KlDRy(FXG$L%S^-?Pztnh(UDN#z@Y$=oTU}t0yl+IE9J(b!# zt_Skwc0}~&$kmpkBL>FZn`33)TD}?34qH6r+a-I)__(-vZU)z=m(9s+&mws1J^g_X z^d8px$Wh3Mr+86v0`gMd;jIHo-blf{=W{Whn*l1GwxZXAf9)4U1PE3Bq!7Nq$|mIF z!K+_OV)2q_*JJj#Eh%o~pVsw<;@{CZMk?-`Q#3*j&hWxIN-t-3H2=s(Od-6WEZ`v? znGRl!QQk|_Z>dtGL!`pWSMiB`#)a{(sLDyw>g7;1Rw;2f)CPOe+ey2%KMiAn#=S_w zU+Qrl)eJF2qyPdB{{SsaLy0Fja$#5+e^Gb2?5=iFk zeH~Sn?@c8x0k|?{d6A@}jdQJq7ca6s)S@TXe@dmpbB~a7L+;y6Dv{!$53hyYCB8Iwnr!N%&9ENN2=?zv>@43*+>P zMve9gY!U8KZW%&O$Fiv_yIpMewYX`1ISWOf$GzY3F6NED#W%V@^~K2ga{|L~y-9b0 zVeAB0+*(>K+1=%yYZ4V+e%S=pVg0=0MosntLX}Qc8w*A=?k#S#?o}B=-4{C@BNJY% z;?XdT<_z&$#9td0?s!~^6C)VxN?OCC=Bv_rfVa6S6LYTp1ug!^&fa!o*g@UBkIPs0 z>E4+i^w5sH@gzoy;%nzvH`L;9A_P$6LIYL9>w1_*4bQbfeRnZ3kP)TG)qx!bXrz?`&u^nt~_*6h|r*mhU!_T z&yuf?Gk7|F9s=N3vbp^M)Mp zWv?bZH)A^2r??B5hG=d^M?`B2rr{MvG3Sd}7sKMMl0F_OO2fYjF`^#a(pr(w_>Jzb z{kMCn)bBGtlP^UIN^1O&7`=&1RmZBcm=V23{l#nR*TmYE#~j~mrPHoyK;qpy4|k`W z9)4#Ywxn|n4!hyT+kS}5#4dCTPF=l3#iA^IPP$!zm)>ZhleiEYmcV`g)}c&V2Ab6E zRs%H7*D##NAzHen1f$?u~@W=vhPDGk0$TCE+*OLX0PYS&PtT< zUY!y6ZRm(gh}%d)$|dKh&P&=Nd36XYH%UDDQ#S2*O-i$lW9PLy#%c6~rIbVbmKm@_ z;Rgdqf{24?TE>dC{ak`VLZp8@j~f3UqR61{D(81qk(WamNd4LmFG@k-vstr8RyKz$ z^Y4d+;6B-V)XcBmCYSxt^kZH2&N`_VbAYuPl$tAT4KF6GoyOHljI>$`{l)z=Q!9@lxSBk#cR!vReK_-k5FAO znXy*>)wo5b(X{Qb93xwaev@}C@v@~bf$rw^-57dkn67SA^3L znUyx3_155$CqygNYer%lg`~}cuF3+Ag`GtqY}PAGMcT;6eIpEQIxYLyb^6dc-mxOp zb2K$Z5g*gD?G+QP6-9mxwN2ZIeLC8srAe2&JN&fSY^g?48;!WB{YKJ(>sz`<#e&;L zZO_E#UZXf&+F|o%!i{gRjS|wr>#)s&#;yG8(M4+k%JD4CNqTI?i0Q{9LYYx82xSd1 zNuEw2;cJ&|g@U6NgKCEh+Xcq=71h%hZ(4}C>DBqYcH35}W4Bph*ot@SU}F*)rm0oZ zsdf%Eh~1xg)~TJ(F!b<6r9&85=55DbTXpf;kYePY8Nt*2F6vg^MXv;TGWUM1$Q4}I z#Si?w^8BW_>q9Q&1J6R2r#)8}Z^kiVA!tZ0^1JRxj`Oy;ck$s?ILv*Og=Qy7dc+4` zit-N-WB6KB^+0aXH$N^Lr(>)#T@6hHCy1KM9lt$R^Cg?V9%Ws|@m-f<=isZ&IK#IB zzs{{|xAv?U5Q>Xt2CX(?6h5G_j4Q> zz#W8o@~*#z`P9`6)D&gYOsc=AaENT`)_&TDPGM(-E|n`3e*GMlZF_-(x+`1K{f(WB zx_B=AyiL83c;g($d{i;iEl>!15VyS5poOroufQWq(0^fGR@+q>9mN8QQn-!A4eXyg z=$k)x*fVffJwzfyCQFPeSK}p=`JL?x^~81u-R${De@!Uc4+J5Yzl;k%zP8+UzeRz^ zLe3;cyzt%Yp54ZS(LGDz3Sqw^iWbAK+9NX>Ya3k-xP61OqiF0<{leCi8a5|$CjA+R z6t5+;!cbEk=az}!&^C<_sSU-R=Iq`3#xn`s7Ys+*KXO|dwW*B;sEg=E)=!us%Lb1l zm~Cg4dBiI}iM8w%sMun=v5H>9vx&khbA0Q77nI#}q@kKnTG-tGpxjPV9gCXnrTBc1 z8Rxy!k&fbD)VGHmlC?xVI_G)nNfi;n@45OWFTD47f(&Um2*6((ZT(r0W0Wbys@|+* zBFCzJuC!;Tc$lfe^;h=!pcVRFzml-2P)nnU#TO*Pm7m$37EA8i?&c-3rVu7LphA9t z*`-5KgEGiwq&+ibokwjF4Bs*AZg&nr@rVBI8#7mYtmzeUjL6iMhMq=B5xq-(<&vr? z`~CL-kAo>?jOeFV4{|ik!w{b-!bkT9Oy3OBEuv#RnU%|lEY}W0`ySh=!zSe-1Faa} zHGEXoGvW=<3Fdy($z;*txcew@KV*?CL~^8a=hyNX+rkj zsb=W97_8OoV))BH?-M@I-n0vMV+mZ)TnVs~lxU3aZnJcwCx6_^wP^By^dj|-)^ES1 z3-ult1_Tx7)U{a9z8$$O{z^#!mp^LhRcw3qP5IkKrt~qm^F)n1u-8jNq~y$7G9_+z zcP4Y~MHhC4_(e-K5y?HVF zLx`TDAEVilXM|DVZa0J`a?{iww?y zH;}Aq7CAeMsh_*=*B^F4YUOe36b^}C#!I=i$w%%;zQ~J?K~O1E$jF?DSLU_VhC``) zsFn8kmkta@O4NM^X|{j%J>zoQY+CQ$41?ZBG(RH!IZ#%6ep;ge+qq_hWRodcp!ns3 z2CqxkM-C(U>Qv*NZbZ{k_Y_OFf6#YeQxh!?Rx2`nM6J&Esr1&>l+ZUrO!)PO_e$t` zh2O)eHxQbtjKlp=7sYNOY*2H}(9xDl*<@7~{aP`@3q&#fd1-1a1@5psUYBT! zzn@a8aEBnwwV%wZN|@yPpnbw{d1N=$w`Yyj_jZg9&gsVRmM^hpq62bdzsblseB+Hs zArZdD(|`T-e)5gCZ}7S8R4pHI*L>Fyo=+?*0-#LY7@LWJAVfi4R zhpBsHJ;~QrdDiNDCq^Wr6h^S&LtwUVzFeoWv0w6RbwkvG&R(tmf!FKDKl0vo78zJg z-uX!CQ~ptrEGCtk@)ujx9iAf9yBi2~`Rb~{D|@+jn@YB53t^SG^TUr9#Y-Wr8MLsI7s z`rR0IBvPOS&b4UL-~|r5gXibS7`}0E+gY=5lNl)>F7b@MozfwD-FZulRkE4v_La^> zW#u&cFSjSOv;1Y!v!;I*#O@QW(^2$L4fgN+d~eti-!dM_zW#viFqm=r?Sb5?*2BZf z=iz4Kp@df;=c=Nj>S?@!g!18`FLO!sR98Ssm~83JsJaj}6mf{ejDLymT#u3Dnr3W& zVogL^bR~94=YSi*JB1sviBW0SSP4E-nkvTogT|A3|vu@ zDjnXBZziwVil=ug4Zw#5S{yv#Nin>zv8JVSXfwH_-JW6Jxu~IGd_<66GsM++aW`k4 zHPb7^xb9otZz?+d+;N5s3zmm;_wg&kKS1!GJdre!H8#*S!I|b2j^g@}`C1OQ*IqdC zeP+Uwx4M2$r~7k@1L2x4noBsC3^gO7hOM0UW~??RkA|`i5}emXN(tIutZYsX=LX7| zqpReWl%A$JvY9F1_ij6JeQ{z?UyzVabNGUMvOHO{)U)C4SYfY45i^`wPS#{3EhC#| zsQUx!W^~#S65KV3`G|^2fyaHE?nkQHz&GLq1vqc+c0Fm&v52zFSteh|AxdCL zjg7o2qh;dWF}++_eH6}Lhllt)x!02>l8L@x&&l-5R3hD;c@HcsrP*YI^ah%ll+uHh zro{G@BFX*?sSTQwn`W@zCMQNS-h4Jxxqq`Qxjm^p=y|oQ9veiF+p)UUqC7AoR+q|r zmW)En$-V3K!J)wBv^rCsv%rD+n7P-VC(B-P{q7qxSrk_m%qyBo%y-dVSw<95cWB!L zxk`Vw@D$(Y2qa1n$hzCVp;k>lrCfI<_nig3;%r@vz{2vM*F5fIEnesa_|j9xx0s==QSpdOGDy;+E-DdBd_dH zqiJ5+d%Yr2Uxf>7STco%C%>8mN8MbcC(PLoxXgwQ7>ekkAMcnY%R9>1$aUI85NPVY zpY-BrUac_LoU;&l3CI8$Gi5J{KLn1JBx{Z6iq!RvTsi<0Rsc@agJP zbZUxI%N<(Q=I7lv8=xLaYJ91KR-~5c-1${U30|rOlQ$hDzPeZe>B^V;GGZGkr(&ymOQd2WjlBI z3%}#4+8=r-h41rL7Swl|?bq<3^pP?WvI5az3w3n8O^akT#e*ceyOs^CSzbG+o{T4d z5>ewb=*rcx4bi1mJg^ksKAF^=d6E$#lVX<4C5oq>pQb4gG`_Ij&Aqy21R5==;b-VEQOw_qBJl58vt#;WVndr^BTql;d?S~dG|H{OJH}>s@NSoys-7NHKwA80M`C7)7 zka0JDB@|9DL*VPKWZIMw&bSubU+>J3Xm;Jw@o<)8SntRl8hJX7!;7muAI zeK8cd!^P6R0S^YwcnPA(PO*ZGQ3SrTFYhN$?j?e8BJWkd)b#=hiOau|e%56j3>R1( zyq|p=++;ICJ(ycUP}y_S@JG7QxMCOYISNtDzPY`f7P$2BFK%Xv`EWCGy=D$2PzAm} zIK)t$WjVrwJ-qz~AzX+joN)(ZG9SwxPcBbBh6ny4%zCfcUsdqCYRD=?Qi9`i#e=Wu z4M#;mFZeZBQ)AavXnXH>JVuDti==f^qq!Ev(*>3G?w~WbP}*5@d@B-rRe5>d-3PDQ z>Dar`jeqJ2U!F^*T10ttXH(jvs_GFxcFdnWs*oazd!_Y(+@3_{kCj#4r3?M#><25U zdUlyk%e}-m4>mG?)}qQY(>IW@+Hb$v7x{>gYc&H#!inz*XO8vDHid=(} zw!OW276hcecZUp&XsSe|$duNkCWnlwh9S^?ij?Wio;%1MdsyO}`Li4AW7Uj?nsJ9g zFMkURO%$!hEPE_FD5?vo9(>D2U%r0C8v%a&j3Zw_rydpgj3o;${yvI}IY+vUew?+f zSRr%^9UsR1IcRd+*E#t8I#pQ1(B+m@nZ_Pt4}lU|Mv2l=)$zl-uoySo<31N zINj`6vDRKX+3#y={CiT7SPRd$vCQ@|IfI)*G6HIk3b2jUDe1G|V%hY|9y&{^;vuee zVQXciC|H+cT)X#@EB{j6rK1oxgxaEEwMwDQF0b@mcEe49#Rb*Q6j<#K-)j!$Zd4xf zU3>7~QjXoI`INSB%rCE2in;cPKBVxcG2l;)^}V@gIQ4vMg-BufPkukH+;@qj8uwdL z%SX|s)hO|Kl=aFB3d4f5BWe$~-MmbZT14io)*8J{r2*IR0 z>U$&~e#iBcooapSQnCQy55~6O=^KRvkN?)bTxR7kJ5-y%lfSFj-a_L)L(e+rQ`2=( zmnFSi#V^-lmc+?DN*_t{YBGM7cu(V+S9XMA^T7V|^-sy@@OfW;^4A>;3M}J$IZBY3 zCl$X`zt6|5c2B0bMdEslmF;lSPI^Ta0t zOYP!U66BWPPG-yBf9pS*fwu7Ku`t!K&@;CZX-8&eU7+i#HEQ8 zq2oNz#+m4V-?}e+;oC+?&)hn}Xfsg|lE0ZVL5QPK8F8S3l=D?-jn94gH)sWOEh zW#^d`V{zo`R#tm`LeN(7WDm%06|EEBAB>=f&wVN-mHtd)y&yc#XW! zlsd66{d}RL?vqzAD%h#9Uiij_l=Vn>J+bGXp)At{ zd^{7={WDnyorCJQz{b+3>qArsZmyfJtQZg=;!s&9B=T?$~bZg$Obf)DYVd@1PW z^*s8ffBoV(QSP6Tz@r&io9vy6LY8WQ-ui_fFuU+NtVyXq36JWO^Gcf?MkjL~;$_Kf|+=CQ^wk_k7vAp|2U}bDxz7pD9vUG!`nd29 z`X#j#2f~y7cH^7{{D_5o)}#QYcBbH{3BoJYjRrT&ny1&iO_0 zdk7rxL8h=9UJPNe?(E|7b=)ua8fs!anXH2F(>2iPM2IluF3+gY#aF_8=zC)~JXsS7 zYQG9}>9#%?iVR@3V{?=1I4>n+jmF;fp2S_T^a(1J%sY_kSn#-1RS{U&YxD^Gtrz@J z1$Ge1R(9|_+=)bCA?e$00k;AC5u_j+P;q+3(Fa&sYqD#&v_!?&)rTw3C-TNv$gp-t)G zO{-0^)k`q&qo289=UlxW(U7>A<+-)Nj>B^ljn15jdo<~teBe_N-~$PBU-V^KLraPKxP)kI`~4mFJ8l$P3UV zneflIrr+5%ZGK*r{b(!cwPj})m+X!`&iQ>5JNU6i39fV!I*v-6=-J=Twe+Dp+XdO- za!KOk;j}hL4l4ZLok)%5yQz2PJgn|u@AH49^ZVpLl%&(|(lx*;f{w^?lde&;Qh(6z zpuLdx=lNTM+!);^+N~AqM4CK)2Jju*(70w3)|QA+$=5^WXpz<`d7BN3gD-ca;%A^d z9i;7%g{;1|@AZhjbKEYx5bKpuhP!={tzGh~n%Cg85JE~pKRkkww>_9g?wz6dqZc12 zcNA&g%taXXv(=Sg4dz|Eqxn0b_acA&wiegp@gfPC@l^4p+G-+{aE_%3^>bGfZTME$ zzLdb>Zm{q|yj$}QNi|2O_E~l8D5jlEBxi|iRrG_t>^ZsN8O6L1d4YmYy9e(b|W2hN)Q^O z-AJpxs#w-+>;>W(Dp-6Nf&CG+ooNJlwv4`~Z}Op{|6nKdQZ4{#+nv>lyhX{iKh3t`c9SZHqbXtLYci!Wg3oIz<0 zX_UfUsOUVwPP3N9k#>8>cJAEo$-?Bor5oksd715`?c|oS_l4>Ybsyn-5F+C%;d+}y zVVu}A8*)A_PR(`!N8qryfR7xF*)P~Q7p_dLOmfPcgVzvfGYK*uA<{DnCxbF65YkgY<6^%p`v zka-8C*~Eh!CEto<9X6I;$fr5xvGKY+|2mcv-59Y|wuwGJ_!N4x&DNVgB_V?S`(fj3 zySGe&V(0?rhSoB^Xw)I!Q7SBE?=P$ z-8FY&=dTTP-2X7=y>{*pyGtOM^W&jZ&E@1ng=y!PoA|@zSFi3NCtLR`uZ2CBsB7OK zZ~3#hrOX&k0DS;HN@unvB=I3UD~O+txAoHg#1s8`ae|iDX+9;EZ|5r3bZG6)8@M~3 zYaR^GOFtAHahKndb|aFITcX*@wiQy}Kt%s>BQ?x14q zqksWT8O=^pl?ips&#K7I`fBt&53Y34`JLFHiqTgWk0=8FG`SG;QcW<+7USc%aY+qh z?_ZneZHSjwLJmJT(Q&2IB7cT)wh!j=$dxe|%+4bkw;R(RXp}?n+he-jzO+Ia#WHUa6mU=sxP8@7G@`j4xD!*al_Qt2nGC}ow0$YB zG$#qCFvVBG`Of%VqVE9jBGlVZy6&xF97*dlZWSEi$fLvcl^C9Ny~L90XE@;(p!bKB zq|xWALSSkz7;ZFYZQTcKcl<1$b#KG*cZEjX<*61G_xIenUM!66I&CyY2YKP{t5$ZI z)RYa>WNm#HSr%QXQb(vYK3-DJsp?mnzijrpr16HuO2OR*0ca3Zd|*$*`~8ah(n?_$ zt?%9XVx9(v%6URx%leYeId2Q(lnzYSUQ@D;PWigG*YYII)(?83T1(jg#TAmLUmi_Z zv@X1uSLxv0Qn;=}9^R(u&GRw)$c)kwvek5S`QUC61Sx#?p5oNS`wjE15(`!HenN*# zPRy)F3GE%l-5HuVaq!IzB}9r_hp?{f(vyDu0kh>$JNDJgA$lrsnzw2C`!$$f z)>wS(ZkVvJtQnqYR476vN))GL)tOT(_L+TF{5%?2c+Fy;qZHRrlQq)Rg8K`KTd;Yu z;g;Jo7p{m4VcL2w1{Kp{LyT%sFX^h5uSsOE@Vb7?ds*OXHlEd*U-^)>W@~;0H=2SS z^ZzNWC;xEqc&{~gL=9(ga@o;)7vZLKu^4+-4@WI#XQ$IGHj>%i@X6pE&U4>YiB_M< z_~bLb;2Kc<#GXcDiijY>)l?kqs+1isP~i0CppYtiGDv7yP--WV-H^y9`@8Dg+}A>? zdYZ*j$XrL^KEJ&Z1nm zb|0QU0+!7@zxjE(xkK{qseh;X?UKT&cA4MfJ$pwAhsxVAg2iX5#yfC2@F7`!OO@;# zdTStecBq6Sdw56ysY>?K^Rie^V2#KSTG5hR3&(5>+$NTU5Jm1fM-}ik!2!a!D zDBZudK-A1-ah-Ug@PpMrg(U2PC~UG>hZ4edoOLL96??1?#QU^p7eMvzm_YpCJe|wQ z4wF(2`9FE_HP_cTB+G8zC~tGETUdk^MUWdAXx)BIi7(71r{7A_zox*AIn$oolTx#Y zcXV%GcM*O8=P8wV06NX70fEqoGVA1~`7~hx!L4P;kgFAC&&+Kpe3t_B@MwX)Q4A4d zwMJ6Es*&XIkI@&1UA_*!hwzs96iLfG~_749DCHKKLULg5brLJGGnh z!m+8gwq5Y=(hNja(3ag8-gwCG)v14TLrdhiT3HXRtEvDbO`eb~G$(+>=o&SC+HByy zLJ03}>9jzx#Bdk(-&ls zLklLzo^o(Oju{K7irEyrHpXhKg5D|xzYmeuH2Dv)?7H>H?4>Kr53G_ER$N{rm)mW7 z{J%<2T-uDP){_gW2#R~{3nRj366Z~HpY*~NBE@eMfY zNyc;e4WLG9o%gx4wfCR5rW#05)&YzBE2Nf&Tc>TIIa;Q zc@vL6Jr%3nYLl?^FFL!f(8kB_0^H2%uny@Gh9rikT#SMxt$+U;r)ikZ}b<1+}S#!m?WEbu(?f1y~VraODy47CC7dGG`9S{qm+QKza@Sk_Ih>i z=mvZ22hPzrUl8F0Z|Y_nhCB(IkJc2!{5 zc%L^K?O_}rIPWCM1$gf2&nEoU{vM-AGnFHHR6CT|#7v6Gl8PGG!|k?;5`PYg1lOO6 zR%VRWF-M5oS$IV$^I`kOqEUw7_`_9)vRAQN#`1#kna0EcrOsqAru{$JM-7OPp_hvm z$3YT`05YGk;RfCn@WVH5q&C%BkRCX(XHXS_m6qn)+PCC((_134U)p-+s%f(tiM=w{H z)qt1N{D>jg_&_~4=tk`cHG$fxP8IELd=`84K-pV`nBA`SqAO~R-ws*XFrQ%+-IozD z(?#7svE0=a>E70mR%eQ9sdhc^H(fkPIF~@Nuuwfhyh(vkbOI8Jm6!8|rZoOQ!%dUT zIpc9vCajLVH95r%2bSzplPIFfq5OlxipNwJ)#w5?c>36=R#HXr0K_dlgP*VoV)qTM(+~y@lH&-3{~;dLS3eYmqrEav`^(T)KZ3dzAeY& zV-A7>Sz0(rE7hxp?vvQ!fa0LOg|qUjdTUSw7}(G;HRo1IO~ zNYonlD!(@w-4oeyY+^$jaPM9{wD~05vqb)F3HL7^wVdHIx<-j=`UFl@$nkc^v8m@c z0}0B6^Pnm|a-Hs%NSHBc{8{|^$ybKOixC|eq7U8n_n=+9z+1|54e#ZfxtvUxL&zNW)LB~Wd08@;@stHizP zbISlZ@Q+XzD?K{0=*@i_-TnxV`B>!ppXWeNRVmR%$N}BPA*EgyC~_bp+}Ii>Q8L?w G34aBd*^0yf literal 0 HcmV?d00001 diff --git a/src/modules/bluez/e_mod_config.c b/src/modules/bluez/e_mod_config.c new file mode 100644 index 000000000..b6e9bf883 --- /dev/null +++ b/src/modules/bluez/e_mod_config.c @@ -0,0 +1,173 @@ +#include "e_mod_main.h" + +extern const char _e_bluez_Name[]; + +struct _E_Config_Dialog_Data +{ + E_Bluez_Instance *inst; + const char *name; + Eina_Bool mode; + unsigned int timeout; + struct { + Evas_Object *label; + Evas_Object *slider; + Evas_Object *help; + } gui; +}; + +/* Local Function Prototypes */ +static void *_create_data(E_Config_Dialog *dialog); +static void _free_data(E_Config_Dialog *dialog, E_Config_Dialog_Data *cfdata); +static Evas_Object *_basic_create(E_Config_Dialog *dialog, Evas *evas, E_Config_Dialog_Data *cfdata); +static int _basic_apply(E_Config_Dialog *dialog, E_Config_Dialog_Data *cfdata); + +E_Config_Dialog * +e_bluez_config_dialog_new(E_Container *con, E_Bluez_Instance *inst) +{ + E_Config_Dialog *dialog; + E_Config_Dialog_View *view; + + if (inst->conf_dialog) + return inst->conf_dialog; + + view = E_NEW(E_Config_Dialog_View, 1); + if (!view) + return NULL; + + view->create_cfdata = _create_data; + view->free_cfdata = _free_data; + view->basic.create_widgets = _basic_create; + view->basic.apply_cfdata = _basic_apply; + + dialog = e_config_dialog_new(con, _("Bluetooth Settings"), + _e_bluez_Name, "e_bluez_config_dialog_new", + e_bluez_theme_path(), 0, view, inst); + + return dialog; +} + +static void * +_create_data(E_Config_Dialog *dialog) +{ + E_Config_Dialog_Data *cfdata; + E_Bluez_Instance *inst; + + cfdata = E_NEW(E_Config_Dialog_Data, 1); + if (!cfdata) + return NULL; + + cfdata->inst = dialog->data; + inst = cfdata->inst; + if (!e_bluez_adapter_discoverable_get(inst->adapter, &cfdata->mode)) + cfdata->mode = 0; + + if (!e_bluez_adapter_discoverable_timeout_get + (inst->adapter, &cfdata->timeout)) + cfdata->timeout = 0; + cfdata->timeout /= 60; + + if (!e_bluez_adapter_name_get(inst->adapter, &cfdata->name)) + cfdata->name = NULL; + cfdata->name = strdup(cfdata->name); + + return cfdata; +} + +static void +_free_data(E_Config_Dialog *dialog, E_Config_Dialog_Data *cfdata) +{ + E_Bluez_Instance *inst = dialog->data; + + inst->conf_dialog = NULL; + E_FREE(cfdata); +} + +static void +_cb_disable_timeout(void *data, Evas_Object *obj __UNUSED__) +{ + E_Config_Dialog_Data *cfdata = data; + Eina_Bool disable = !cfdata->mode; + + e_widget_disabled_set(cfdata->gui.label, disable); + e_widget_disabled_set(cfdata->gui.slider, disable); + e_widget_disabled_set(cfdata->gui.help, disable); +} + +static Evas_Object * +_basic_create(E_Config_Dialog *dialog __UNUSED__, Evas *evas, E_Config_Dialog_Data *cfdata) +{ + Evas_Object *o, *ob; + Evas_Object *check; + char buf[48]; + const char *address; + + o = e_widget_list_add(evas, 0, 0); + + if (!e_bluez_adapter_address_get(cfdata->inst->adapter, &address)) + address = NULL; + + ob = e_widget_label_add(evas, _("Name")); + e_widget_list_object_append(o, ob, 1, 1, 0.5); + + ob = e_widget_entry_add(evas, (char **)&cfdata->name, NULL, NULL, NULL); + e_widget_list_object_append(o, ob, 1, 1, 0.5); + + check = e_widget_check_add + (evas, _("Discoverable mode"), (int *)&cfdata->mode); + e_widget_list_object_append(o, check, 1, 1, 0.5); + + ob = e_widget_label_add(evas, _("Discovarable Timeout")); + cfdata->gui.label = ob; + e_widget_list_object_append(o, ob, 1, 1, 0.5); + + ob = e_widget_slider_add(evas, 1, 0, _("%1.0f minutes"), 0, 30, 1, 0, + NULL, (int*)&cfdata->timeout, 100); + e_widget_slider_special_value_add(ob, 0.0, 0.0, _("Forever")); + cfdata->gui.slider = ob; + e_widget_list_object_append(o, ob, 1, 1, 0.5); + + e_widget_on_change_hook_set(check, _cb_disable_timeout, cfdata); + _cb_disable_timeout(cfdata, NULL); + + snprintf(buf, sizeof(buf), _("MAC Address: %s"), address); + ob = e_widget_label_add(evas, buf); + e_widget_list_object_append(o, ob, 1, 1, 0.5); + + return o; +} + +static void +_method_success_check(void *data, DBusMessage *msg __UNUSED__, DBusError *error) +{ + const char *name = data; + + if ((!error) || (!dbus_error_is_set(error))) + return; + + ERR("method %s() finished with error: %s %s\n", + name, error->name, error->message); + dbus_error_free(error); +} + +static int +_basic_apply(E_Config_Dialog *dialog __UNUSED__, E_Config_Dialog_Data *cfdata) +{ + E_Bluez_Instance *inst = cfdata->inst; + + if (!e_bluez_adapter_discoverable_set + (inst->adapter, cfdata->mode, + _method_success_check, "e_bluez_adapter_discoverable_get")) + ERR("Can't set Discoverable on adapter"); + + if (!e_bluez_adapter_discoverable_timeout_set + (inst->adapter, cfdata->timeout * 60, + _method_success_check, "e_bluez_adapter_discoverable_timeout_get")) + ERR("Can't set DiscoverableTimeout on adapter"); + + if (!e_bluez_adapter_name_set + (inst->adapter, cfdata->name, + _method_success_check, "e_bluez_adapter_name_get")) + ERR("Can't set Name on adapter"); + + return 1; +} diff --git a/src/modules/bluez/e_mod_main.c b/src/modules/bluez/e_mod_main.c new file mode 100644 index 000000000..6fdf5216b --- /dev/null +++ b/src/modules/bluez/e_mod_main.c @@ -0,0 +1,1358 @@ +/* + * TODO: + * + * HIGH: + * + * - check why return NULL from method call triggers cancel error + * after timeout. + * - find out why alias == address in _bluez_request_pincode_cb + * - more complete agent support (handle requests from devices) + * - handle device-disappeared events + * - icon with device state (trusted, connected, paired) + * + * LOW: + * + * - configure (probably module) timeout to trigger automatic rescan. + * - gadgets to show different adapters (see mixer module configuration) + * - module to choose the default adapter (see mixer module configuration) + * - icon with device class + */ +#include "e.h" +#include "e_mod_main.h" + +static E_Module *bluez_mod = NULL; +static char tmpbuf[PATH_MAX]; /* general purpose buffer, just use immediately */ + +static const char _e_bluez_agent_path[] = "/org/enlightenment/bluez/Agent"; +const char _e_bluez_name[] = "bluez"; +const char _e_bluez_Name[] = "Bluetooth Manager"; +int _e_bluez_log_dom = -1; + +static void _bluez_gadget_update(E_Bluez_Instance *inst); +static void _bluez_tip_update(E_Bluez_Instance *inst); +static void _bluez_popup_update(E_Bluez_Instance *inst); + +struct bluez_pincode_data +{ + void (*cb)(struct bluez_pincode_data *d); + DBusMessage *msg; + E_Bluez_Module_Context *ctxt; + char *pincode; + const char *alias; + E_Dialog *dia; + Evas_Object *entry; + Eina_Bool canceled; +}; + +const char * +e_bluez_theme_path(void) +{ +#define TF "/e-module-bluez.edj" + size_t dirlen; + + dirlen = strlen(bluez_mod->dir); + if (dirlen >= sizeof(tmpbuf) - sizeof(TF)) + return NULL; + + memcpy(tmpbuf, bluez_mod->dir, dirlen); + memcpy(tmpbuf + dirlen, TF, sizeof(TF)); + + return tmpbuf; +#undef TF +} + +static void +_bluez_devices_clear(E_Bluez_Instance *inst) +{ + E_Bluez_Instance_Device *d; + EINA_LIST_FREE(inst->devices, d) + { + eina_stringshare_del(d->address); + eina_stringshare_del(d->alias); + free(d); + } + inst->address = NULL; + inst->alias = NULL; +} + +static void +_bluez_discovery_cb(void *data, DBusMessage *msg __UNUSED__, DBusError *error) +{ + E_Bluez_Instance *inst= data; + char *label; + + if (error && dbus_error_is_set(error)) + { + _bluez_dbus_error_show(_("Cannot change adapter's discovery."), error); + dbus_error_free(error); + return; + } + + inst->discovering = !inst->discovering; + + label = !inst->discovering ? _("Start Scan") : _("Stop Scan"); + e_widget_button_label_set(inst->ui.button, label); +} + +static void +_bluez_create_paired_device_cb(void *data, DBusMessage *msg __UNUSED__, DBusError *error) +{ + const char *alias = data; + + if (error && dbus_error_is_set(error)) + { + if (strcmp(error->name, "org.bluez.Error.AlreadyExists") != 0) + _bluez_dbus_error_show(_("Cannot pair with device."), error); + dbus_error_free(error); + eina_stringshare_del(alias); + return; + } + + e_util_dialog_show + (_("Bluetooth Manager"), _("Device '%s' successfully paired."), alias); + eina_stringshare_del(alias); +} + +static void +_bluez_toggle_powered_cb(void *data, DBusMessage *msg __UNUSED__, DBusError *error) +{ + E_Bluez_Instance *inst = data; + + if ((!error) || (!dbus_error_is_set(error))) + { + inst->powered_pending = EINA_FALSE; + inst->powered = !inst->powered; + + if (!inst->powered) + { + _bluez_devices_clear(inst); + + if (inst->popup) + _bluez_popup_update(inst); + } + + _bluez_gadget_update(inst); + return; + } + + _bluez_dbus_error_show(_("Cannot toggle adapter's powered."), error); + dbus_error_free(error); +} + +void +_bluez_toggle_powered(E_Bluez_Instance *inst) +{ + Eina_Bool powered; + + if ((!inst) || (!inst->ctxt->has_manager)) + { + _bluez_operation_error_show(_("BlueZ Daemon is not running.")); + return; + } + + if (!e_bluez_adapter_powered_get(inst->adapter, &powered)) + { + _bluez_operation_error_show + (_("Query adapter's powered.")); + return; + } + + powered = !powered; + + if (!e_bluez_adapter_powered_set + (inst->adapter, powered, _bluez_toggle_powered_cb, inst)) + { + _bluez_operation_error_show(_("Query adapter's powered.")); + return; + } +} + +static void +_bluez_cb_toggle_powered(E_Object *obj __UNUSED__, const char *params __UNUSED__) +{ + E_Bluez_Module_Context *ctxt; + + if (!bluez_mod) + return; + + ctxt = bluez_mod->data; + if (ctxt->default_instance) + _bluez_toggle_powered(ctxt->default_instance); +} + +static void _bluez_popup_del(E_Bluez_Instance *inst); + +static int +_bluez_popup_input_window_mouse_up_cb(void *data, int type __UNUSED__, void *event) +{ + Ecore_Event_Mouse_Button *ev = event; + E_Bluez_Instance *inst = data; + + if (ev->window != inst->ui.input.win) + return 1; + + _bluez_popup_del(inst); + + return 1; +} + +static int +_bluez_popup_input_window_key_down_cb(void *data, int type __UNUSED__, void *event) +{ + Ecore_Event_Key *ev = event; + E_Bluez_Instance *inst = data; + const char *keysym; + + if (ev->window != inst->ui.input.win) + return 1; + + keysym = ev->key; + if (strcmp(keysym, "Escape") == 0) + _bluez_popup_del(inst); + + return 1; +} + +static void +_bluez_popup_input_window_destroy(E_Bluez_Instance *inst) +{ + ecore_x_window_free(inst->ui.input.win); + inst->ui.input.win = 0; + + ecore_event_handler_del(inst->ui.input.mouse_up); + inst->ui.input.mouse_up = NULL; + + ecore_event_handler_del(inst->ui.input.key_down); + inst->ui.input.key_down = NULL; +} + +static void +_bluez_popup_input_window_create(E_Bluez_Instance *inst) +{ + Ecore_X_Window_Configure_Mask mask; + Ecore_X_Window w, popup_w; + E_Manager *man; + + man = e_manager_current_get(); + + w = ecore_x_window_input_new(man->root, 0, 0, man->w, man->h); + mask = (ECORE_X_WINDOW_CONFIGURE_MASK_STACK_MODE | + ECORE_X_WINDOW_CONFIGURE_MASK_SIBLING); + popup_w = inst->popup->win->evas_win; + ecore_x_window_configure(w, mask, 0, 0, 0, 0, 0, popup_w, + ECORE_X_WINDOW_STACK_BELOW); + ecore_x_window_show(w); + + inst->ui.input.mouse_up = + ecore_event_handler_add(ECORE_EVENT_MOUSE_BUTTON_UP, + _bluez_popup_input_window_mouse_up_cb, inst); + + inst->ui.input.key_down = + ecore_event_handler_add(ECORE_EVENT_KEY_DOWN, + _bluez_popup_input_window_key_down_cb, inst); + + inst->ui.input.win = w; +} + +static void +_bluez_popup_cb_powered_changed(void *data, Evas_Object *obj) +{ + E_Bluez_Instance *inst = data; + E_Bluez_Module_Context *ctxt = inst->ctxt; + Eina_Bool powered = e_widget_check_checked_get(obj); + + if ((!ctxt) || (!ctxt->has_manager)) + { + _bluez_operation_error_show(_("BlueZ Daemon is not running.")); + return; + } + + if (!e_bluez_adapter_powered_set + (inst->adapter, powered, _bluez_toggle_powered_cb, inst)) + { + _bluez_operation_error_show + (_("Cannot toggle adapter's powered.")); + return; + } + + inst->powered_pending = EINA_TRUE; +} + +static void +_bluez_pincode_ask_cb(struct bluez_pincode_data *d) +{ + DBusMessage *reply; + + if (!d->pincode) + { + e_util_dialog_show(_("Bluetooth Manager"), _("Invalid Pin Code.")); + return; + } + + reply = dbus_message_new_method_return(d->msg); + dbus_message_append_args + (reply, DBUS_TYPE_STRING, &d->pincode, DBUS_TYPE_INVALID); + + dbus_message_set_no_reply(reply, EINA_TRUE); + e_dbus_message_send(d->ctxt->agent.conn, reply, NULL, -1, NULL); +} + +static void +bluez_pincode_ask_ok(void *data, E_Dialog *dia) +{ + struct bluez_pincode_data *d = data; + d->canceled = EINA_FALSE; + e_object_del(E_OBJECT(dia)); +} + +static void +bluez_pincode_ask_cancel(void *data, E_Dialog *dia) +{ + struct bluez_pincode_data *d = data; + d->canceled = EINA_TRUE; + e_object_del(E_OBJECT(dia)); +} + +static void +bluez_pincode_ask_del(void *data) +{ + E_Dialog *dia = data; + struct bluez_pincode_data *d = e_object_data_get(E_OBJECT(dia)); + + if (!d->canceled) + d->cb(d); + + d->ctxt->agent.pending = eina_list_remove(d->ctxt->agent.pending, dia); + + free(d->pincode); + dbus_message_unref(d->msg); + eina_stringshare_del(d->alias); + E_FREE(d); +} + +static void +bluez_pincode_ask_key_down(void *data, Evas *e __UNUSED__, Evas_Object *o __UNUSED__, void *event) +{ + Evas_Event_Key_Down *ev = event; + struct bluez_pincode_data *d = data; + + if (strcmp(ev->keyname, "Return") == 0) + bluez_pincode_ask_ok(d, d->dia); + else if (strcmp(ev->keyname, "Escape") == 0) + bluez_pincode_ask_cancel(d, d->dia); +} + +static void +bluez_pincode_ask(void (*cb)(struct bluez_pincode_data *), DBusMessage *msg, const char *alias, E_Bluez_Module_Context *ctxt) +{ + struct bluez_pincode_data *d; + Evas_Object *list, *o; + Evas *evas; + char buf[512]; + int mw, mh; + + if (!cb) + return; + + d = E_NEW(struct bluez_pincode_data, 1); + if (!d) + return; + + d->cb = cb; + d->ctxt = ctxt; + d->alias = eina_stringshare_add(alias); + d->msg = dbus_message_ref(msg); + d->canceled = EINA_TRUE; /* closing the dialog defaults to cancel */ + d->dia = e_dialog_new(NULL, "E", "bluez_ask_pincode"); + + snprintf(buf, sizeof(buf), _("Pairing with device '%s'"), alias); + e_dialog_title_set(d->dia, buf); + e_dialog_icon_set(d->dia, "dialog-ask", 32); + e_dialog_border_icon_set(d->dia, "dialog-ask"); + + evas = d->dia->win->evas; + + list = e_widget_list_add(evas, 0, 0); + + o = edje_object_add(evas); + e_theme_edje_object_set(o, "base/theme/dialog", + "e/widgets/dialog/text"); + snprintf(buf, sizeof(buf), + _("Enter the PIN code: ")); + edje_object_part_text_set(o, "e.textblock.message", buf); + edje_object_size_min_calc(o, &mw, &mh); + evas_object_size_hint_min_set(o, mw, mh); + evas_object_resize(o, mw, mh); + evas_object_show(o); + e_widget_list_object_append(list, o, 1, 1, 0.5); + + d->entry = o = e_widget_entry_add(evas, &d->pincode, NULL, NULL, NULL); + e_widget_entry_password_set(o, 0); + evas_object_show(o); + e_widget_list_object_append(list, o, 1, 0, 0.0); + + e_widget_size_min_get(list, &mw, &mh); + if (mw < 200) + mw = 200; + if (mh < 60) + mh = 60; + e_dialog_content_set(d->dia, list, mw, mh); + + e_dialog_button_add + (d->dia, _("Ok"), NULL, bluez_pincode_ask_ok, d); + e_dialog_button_add + (d->dia, _("Cancel"), NULL, bluez_pincode_ask_cancel, d); + + evas_object_event_callback_add + (d->dia->bg_object, EVAS_CALLBACK_KEY_DOWN, + bluez_pincode_ask_key_down, d); + + e_object_del_attach_func_set + (E_OBJECT(d->dia), bluez_pincode_ask_del); + e_object_data_set(E_OBJECT(d->dia), d); + + e_dialog_button_focus_num(d->dia, 0); + e_widget_focus_set(d->entry, 1); + + e_win_centered_set(d->dia->win, 1); + e_dialog_show(d->dia); + + ctxt->agent.pending = eina_list_append(ctxt->agent.pending, d->dia); +} + +static DBusMessage* +_bluez_request_pincode_cb(E_DBus_Object *obj, DBusMessage *msg) +{ + E_Bluez_Module_Context *ctxt = e_dbus_object_data_get(obj); + E_Bluez_Element *element; + const char *path; + const char *alias; + + // TODO: seems that returning NULL is causing pin code rquest to be canceled! + + if (dbus_message_get_args(msg, NULL, DBUS_TYPE_OBJECT_PATH, &path, + DBUS_TYPE_INVALID) == FALSE) + return NULL; + + element = e_bluez_device_get(path); + if (!element) + alias = path; + else + { + if (!e_bluez_device_alias_get(element, &alias)) + { + if (!e_bluez_device_name_get(element, &alias)) + alias = path; + } + } + // TODO: find out why alias == address, then remove debug: + fprintf(stderr, ">>> request pin code of '%s' (%s)\n", alias, path); + bluez_pincode_ask(_bluez_pincode_ask_cb, msg, alias, ctxt); + return NULL; +} + +static void +_bluez_popup_cb_scan(void *data, void *data2 __UNUSED__) +{ + E_Bluez_Instance *inst = data; + int ret; + + if (inst->discovering) + ret = e_bluez_adapter_stop_discovery + (inst->adapter, _bluez_discovery_cb, inst); + else + { + inst->last_scan = ecore_loop_time_get(); + + _bluez_devices_clear(inst); + + ret = e_bluez_adapter_start_discovery + (inst->adapter, _bluez_discovery_cb, inst); + + _bluez_popup_update(inst); + } + + if (!ret) + ERR("Failed on discovery procedure"); +} + +static void +_bluez_popup_cb_controls(void *data, void *data2 __UNUSED__) +{ + E_Bluez_Instance *inst = data; + if (inst->popup) + _bluez_popup_del(inst); + if (inst->conf_dialog) + return; + inst->conf_dialog = e_bluez_config_dialog_new(NULL, inst); +} + +static void +_bluez_popup_device_selected(void *data) +{ + E_Bluez_Instance *inst = data; + const char *address = inst->address; + const char *alias; + const char *cap = "DisplayYesNo"; + const E_Bluez_Instance_Device *d; + const Eina_List *l; + + if (inst->popup) + _bluez_popup_del(inst); + + if (!address) + { + ERR("no device selected for pairing."); + return; + } + + inst->alias = address; + EINA_LIST_FOREACH(inst->devices, l, d) + { + if (address == d->alias) + { + inst->alias = d->alias; + break; + } + } + + if (!inst->alias) + { + ERR("device %s does not have an alias.", address); + return; + } + + alias = eina_stringshare_ref(inst->alias); + if (!e_bluez_adapter_create_paired_device + (inst->adapter, _e_bluez_agent_path, cap, address, + _bluez_create_paired_device_cb, alias)) + { + eina_stringshare_del(alias); + return; + } +} + +static int +_bluez_event_devicefound(void *data, int type __UNUSED__, void *event __UNUSED__) +{ + E_Bluez_Module_Context *ctxt = data; + E_Bluez_Device_Found *device = event; + E_Bluez_Instance *inst; + const Eina_List *l_inst; + const char *alias; + + // TODO: get properties such as paired, connected, trusted, class, icon... + // TODO: check if the adapter contains device->name and if so get path. + + alias = e_bluez_devicefound_alias_get(device); + + EINA_LIST_FOREACH(ctxt->instances, l_inst, inst) + { + const Eina_List *l_dev; + E_Bluez_Instance_Device *dev; + Eina_Bool found = EINA_FALSE; + + if (inst->adapter != device->adapter) continue; + + EINA_LIST_FOREACH(inst->devices, l_dev, dev) + { + if (dev->address == device->name) + { + found = EINA_TRUE; + break; + } + } + + if (found) continue; + + dev = malloc(sizeof(E_Bluez_Instance_Device)); + if (!dev) continue; + + dev->address = eina_stringshare_ref(device->name); + dev->alias = eina_stringshare_ref(alias); + + inst->devices = eina_list_append(inst->devices, dev); + + if (inst->ui.list) + { + e_widget_ilist_append + (inst->ui.list, NULL, dev->alias, + _bluez_popup_device_selected, inst, dev->address); + e_widget_ilist_go(inst->ui.list); + } + } + + return 1; +} + +static void +_bluez_popup_update(E_Bluez_Instance *inst) +{ + Evas_Object *list = inst->ui.list; + int selected; + const char *label; + E_Bluez_Instance_Device *d; + Eina_List *l; + + /* TODO: replace this with a scroller + list of edje + * objects that are more full of features + */ + selected = e_widget_ilist_selected_get(list); + e_widget_ilist_freeze(list); + e_widget_ilist_clear(list); + + EINA_LIST_FOREACH(inst->devices, l, d) + { + e_widget_ilist_append + (inst->ui.list, NULL, d->alias, + _bluez_popup_device_selected, inst, d->address); + } + + if (selected >= 0) + { + inst->first_selection = EINA_TRUE; + e_widget_ilist_selected_set(list, selected); + } + else + inst->first_selection = EINA_FALSE; + + e_widget_ilist_go(list); + + e_widget_check_checked_set(inst->ui.powered, inst->powered); + label = inst->discovering ? _("Stop Scan") : _("Start Scan"); + e_widget_button_label_set(inst->ui.button, label); + e_widget_disabled_set(inst->ui.button, !inst->powered); +} + +static void +_bluez_popup_del(E_Bluez_Instance *inst) +{ + _bluez_popup_input_window_destroy(inst); + e_object_del(E_OBJECT(inst->popup)); + inst->popup = NULL; +} + +static void +_bluez_popup_new(E_Bluez_Instance *inst) +{ + Evas_Object *ol; + Evas *evas; + Evas_Coord mw, mh; + const char *label; + Eina_Bool b, needs_scan = EINA_FALSE; + + if (inst->popup) + { + e_gadcon_popup_show(inst->popup); + return; + } + + if (!e_bluez_adapter_discovering_get(inst->adapter, &b)) + { + _bluez_operation_error_show(_("Can't get Discovering property")); + return; + } + inst->discovering = b; + // maybe auto-scan if did not in the last 30 minutes? + // seems scan will hurt things like bluetooth audio playback, so don't do it + if ((!inst->discovering) && (inst->last_scan <= 0.0) && (inst->ui.powered)) + { + label = _("Stop Scan"); + needs_scan = EINA_TRUE; + } + else + label = inst->discovering ? _("Stop Scan") : _("Start Scan"); + + inst->popup = e_gadcon_popup_new(inst->gcc); + evas = inst->popup->win->evas; + + ol = e_widget_list_add(evas, 0, 0); + + // TODO: get this size from edj + inst->ui.list = e_widget_ilist_add(evas, 32, 32, &inst->address); + e_widget_size_min_set(inst->ui.list, 180, 100); + e_widget_list_object_append(ol, inst->ui.list, 1, 1, 0.5); + + inst->powered = inst->powered; + inst->ui.powered = e_widget_check_add(evas, _("Powered"), &inst->powered); + e_widget_on_change_hook_set + (inst->ui.powered, _bluez_popup_cb_powered_changed, inst); + e_widget_list_object_append(ol, inst->ui.powered, 1, 0, 0.5); + + inst->ui.button = e_widget_button_add + (evas, label, NULL, _bluez_popup_cb_scan, inst, NULL); + e_widget_list_object_append(ol, inst->ui.button, 1, 0, 0.5); + + inst->ui.control = e_widget_button_add + (evas, _("Controls"), NULL, _bluez_popup_cb_controls, inst, NULL); + e_widget_list_object_append(ol, inst->ui.control, 1, 0, 0.5); + + _bluez_popup_update(inst); + + e_widget_size_min_get(ol, &mw, &mh); + if (mh < 200) mh = 200; + if (mw < 200) mw = 200; + e_widget_size_min_set(ol, mw, mh); + + e_gadcon_popup_content_set(inst->popup, ol); + e_gadcon_popup_show(inst->popup); + _bluez_popup_input_window_create(inst); + + if (needs_scan) _bluez_popup_cb_scan(inst, NULL); +} + +static void +_bluez_menu_cb_post(void *data, E_Menu *menu __UNUSED__) +{ + E_Bluez_Instance *inst = data; + if ((!inst) || (!inst->menu)) + return; + if (inst->menu) + { + e_object_del(E_OBJECT(inst->menu)); + inst->menu = NULL; + } +} + +static void +_bluez_menu_cb_cfg(void *data, E_Menu *menu __UNUSED__, E_Menu_Item *mi __UNUSED__) +{ + E_Bluez_Instance *inst = data; + if (inst->popup) + _bluez_popup_del(inst); + if (inst->conf_dialog) + return; + inst->conf_dialog = e_bluez_config_dialog_new(NULL, inst); +} + +static void +_bluez_menu_new(E_Bluez_Instance *inst, Evas_Event_Mouse_Down *ev) +{ + E_Zone *zone; + E_Menu *mn; + E_Menu_Item *mi; + int x, y; + + zone = e_util_zone_current_get(e_manager_current_get()); + + mn = e_menu_new(); + e_menu_post_deactivate_callback_set(mn, _bluez_menu_cb_post, inst); + inst->menu = mn; + + mi = e_menu_item_new(mn); + e_menu_item_label_set(mi, _("Settings")); + e_util_menu_item_theme_icon_set(mi, "configure"); + e_menu_item_callback_set(mi, _bluez_menu_cb_cfg, inst); + + e_gadcon_client_util_menu_items_append(inst->gcc, mn, 0); + e_gadcon_canvas_zone_geometry_get(inst->gcc->gadcon, &x, &y, NULL, NULL); + e_menu_activate_mouse(mn, zone, x + ev->output.x, y + ev->output.y, + 1, 1, E_MENU_POP_DIRECTION_AUTO, ev->timestamp); + evas_event_feed_mouse_up(inst->gcc->gadcon->evas, ev->button, + EVAS_BUTTON_NONE, ev->timestamp, NULL); +} + +static void +_bluez_tip_new(E_Bluez_Instance *inst) +{ + Evas *e; + + inst->tip = e_gadcon_popup_new(inst->gcc); + if (!inst->tip) return; + + e = inst->tip->win->evas; + + inst->o_tip = edje_object_add(e); + e_theme_edje_object_set(inst->o_tip, "base/theme/modules/bluez/tip", + "e/modules/bluez/tip"); + + _bluez_tip_update(inst); + + e_gadcon_popup_content_set(inst->tip, inst->o_tip); + e_gadcon_popup_show(inst->tip); +} + +static void +_bluez_tip_del(E_Bluez_Instance *inst) +{ + evas_object_del(inst->o_tip); + e_object_del(E_OBJECT(inst->tip)); + inst->tip = NULL; + inst->o_tip = NULL; +} + +static void +_bluez_cb_mouse_down(void *data, Evas *evas __UNUSED__, Evas_Object *obj __UNUSED__, void *event) +{ + E_Bluez_Instance *inst; + Evas_Event_Mouse_Down *ev; + + inst = data; + if (!inst) + return; + + ev = event; + if (ev->button == 1) + { + if (!inst->popup) + _bluez_popup_new(inst); + else + _bluez_popup_del(inst); + } + else if (ev->button == 2) + _bluez_toggle_powered(inst); + else if ((ev->button == 3) && (!inst->menu)) + _bluez_menu_new(inst, ev); +} + +static void +_bluez_cb_mouse_in(void *data, Evas *evas __UNUSED__, Evas_Object *obj __UNUSED__, void *event __UNUSED__) +{ + E_Bluez_Instance *inst = data; + + if (inst->tip) + return; + + _bluez_tip_new(inst); +} + +static void +_bluez_cb_mouse_out(void *data, Evas *evas __UNUSED__, Evas_Object *obj __UNUSED__, void *event __UNUSED__) +{ + E_Bluez_Instance *inst = data; + + + if (!inst->tip) + return; + + _bluez_tip_del(inst); +} + +static void +_bluez_edje_view_update(E_Bluez_Instance *inst, Evas_Object *o) +{ + E_Bluez_Module_Context *ctxt = inst->ctxt; + const char *name; + + if (!ctxt->has_manager) + { + edje_object_part_text_set(o, "e.text.powered", ""); + edje_object_part_text_set(o, "e.text.status", ""); + edje_object_signal_emit(o, "e,changed,off", "e"); + edje_object_part_text_set(o, "e.text.name", _("No Bluetooth daemon")); + edje_object_signal_emit(o, "e,changed,name", "e"); + return; + } + + if (!e_bluez_adapter_name_get(inst->adapter, &name)) + name = ""; + edje_object_part_text_set(o, "e.text.name", name); + edje_object_signal_emit(o, "e,changed,name", "e"); + + if (inst->powered) + { + if (inst->discoverable) + { + edje_object_signal_emit(o, "e,changed,powered", "e"); + edje_object_part_text_set + (o, "e.text.status", + _("Bluetooth is powered and discoverable.")); + } + else + { + edje_object_signal_emit(o, "e,changed,hidden", "e"); + edje_object_part_text_set + (o, "e.text.status", _("Bluetooth is powered and hidden.")); + } + } + else + { + edje_object_signal_emit(o, "e,changed,off", "e"); + edje_object_part_text_set(o, "e.text.status", + _("Bluetooth is off.")); + } +} + +static void +_bluez_tip_update(E_Bluez_Instance *inst) +{ + _bluez_edje_view_update(inst, inst->o_tip); +} + +static void +_bluez_gadget_update(E_Bluez_Instance *inst) +{ + E_Bluez_Module_Context *ctxt = inst->ctxt; + + if (!ctxt->has_manager && inst->popup) + _bluez_popup_del(inst); + + if (inst->popup) + _bluez_popup_update(inst); + if (inst->tip) + _bluez_tip_update(inst); + + _bluez_edje_view_update(inst, inst->ui.gadget); +} + +/* Gadcon Api Functions */ + +static E_Gadcon_Client * +_gc_init(E_Gadcon *gc, const char *name, const char *id, const char *style) +{ + E_Bluez_Instance *inst; + E_Bluez_Module_Context *ctxt; + + if (!bluez_mod) + return NULL; + + ctxt = bluez_mod->data; + + inst = E_NEW(E_Bluez_Instance, 1); + inst->ctxt = ctxt; + inst->ui.gadget = edje_object_add(gc->evas); + e_theme_edje_object_set(inst->ui.gadget, "base/theme/modules/bluez", + "e/modules/bluez/main"); + + inst->gcc = e_gadcon_client_new(gc, name, id, style, inst->ui.gadget); + inst->gcc->data = inst; + + evas_object_event_callback_add + (inst->ui.gadget, EVAS_CALLBACK_MOUSE_DOWN, _bluez_cb_mouse_down, inst); + evas_object_event_callback_add + (inst->ui.gadget, EVAS_CALLBACK_MOUSE_IN, _bluez_cb_mouse_in, inst); + evas_object_event_callback_add + (inst->ui.gadget, EVAS_CALLBACK_MOUSE_OUT, _bluez_cb_mouse_out, inst); + + // TODO: instead of getting the default adapter, get the adapter for + // each instance. See the mixer module. + inst->adapter = e_bluez_adapter_get(ctxt->default_adapter); + if (inst->adapter) + { + Eina_Bool powered, discoverable, discovering; + + if (e_bluez_adapter_powered_get(inst->adapter, &powered)) + inst->powered = powered; + + if (e_bluez_adapter_discoverable_get(inst->adapter, &discoverable)) + inst->discoverable = discoverable; + + if (e_bluez_adapter_discovering_get(inst->adapter, &discovering)) + inst->discovering = discovering; + } + + if (!ctxt->default_instance) + ctxt->default_instance = inst; + + _bluez_gadget_update(inst); + + ctxt->instances = eina_list_append(ctxt->instances, inst); + + return inst->gcc; +} + +static void +_gc_shutdown(E_Gadcon_Client *gcc) +{ + E_Bluez_Module_Context *ctxt; + E_Bluez_Instance *inst; + + if (!bluez_mod) + return; + + ctxt = bluez_mod->data; + if (!ctxt) + return; + + inst = gcc->data; + if (!inst) + return; + + if (inst->menu) + { + e_menu_post_deactivate_callback_set(inst->menu, NULL, NULL); + e_object_del(E_OBJECT(inst->menu)); + } + evas_object_del(inst->ui.gadget); + + _bluez_devices_clear(inst); + + ctxt->instances = eina_list_remove(ctxt->instances, inst); + + if (ctxt->default_instance == inst) + ctxt->default_instance = NULL; + + E_FREE(inst); +} + +static void +_gc_orient(E_Gadcon_Client *gcc, E_Gadcon_Orient orient __UNUSED__) +{ + e_gadcon_client_aspect_set(gcc, 16, 16); + e_gadcon_client_min_size_set(gcc, 16, 16); +} + +static char * +_gc_label(E_Gadcon_Client_Class *client_class __UNUSED__) +{ + return _(_e_bluez_Name); +} + +static Evas_Object * +_gc_icon(E_Gadcon_Client_Class *client_class __UNUSED__, Evas *evas) +{ + Evas_Object *o; + + o = edje_object_add(evas); + edje_object_file_set(o, e_bluez_theme_path(), "icon"); + return o; +} + +static const char * +_gc_id_new(E_Gadcon_Client_Class *client_class __UNUSED__) +{ + E_Bluez_Module_Context *ctxt; + + if (!bluez_mod) + return NULL; + + ctxt = bluez_mod->data; + if (!ctxt) + return NULL; + + snprintf(tmpbuf, sizeof(tmpbuf), "bluez.%d", + eina_list_count(ctxt->instances)); + return tmpbuf; +} + +static const E_Gadcon_Client_Class _gc_class = + { + GADCON_CLIENT_CLASS_VERSION, _e_bluez_name, + { + _gc_init, _gc_shutdown, _gc_orient, _gc_label, _gc_icon, _gc_id_new, NULL, + e_gadcon_site_is_not_toolbar + }, + E_GADCON_CLIENT_STYLE_PLAIN + }; + +EAPI E_Module_Api e_modapi = {E_MODULE_API_VERSION, _e_bluez_Name}; + +static const char _act_toggle_powered[] = "toggle_powered"; +static const char _lbl_toggle_powered[] = "Toggle Powered"; + +static void +_bluez_actions_register(E_Bluez_Module_Context *ctxt) +{ + ctxt->actions.toggle_powered = e_action_add(_act_toggle_powered); + if (ctxt->actions.toggle_powered) + { + ctxt->actions.toggle_powered->func.go = + _bluez_cb_toggle_powered; + e_action_predef_name_set + (_(_e_bluez_Name), _(_lbl_toggle_powered), _act_toggle_powered, + NULL, NULL, 0); + } +} + +static void +_bluez_actions_unregister(E_Bluez_Module_Context *ctxt) +{ + if (ctxt->actions.toggle_powered) + { + e_action_predef_name_del(_(_e_bluez_Name), _(_lbl_toggle_powered)); + e_action_del(_act_toggle_powered); + } +} + +static int +_bluez_manager_changed_do(void *data) +{ + E_Bluez_Module_Context *ctxt = data; + + //FIXME: reload the default adapter maybe? + + ctxt->poller.manager_changed = NULL; + return 0; +} + +static void +_bluez_manager_changed(void *data, const E_Bluez_Element *element __UNUSED__) +{ + E_Bluez_Module_Context *ctxt = data; + if (ctxt->poller.manager_changed) + ecore_poller_del(ctxt->poller.manager_changed); + ctxt->poller.manager_changed = ecore_poller_add + (ECORE_POLLER_CORE, 1, _bluez_manager_changed_do, ctxt); +} + +static void +_properties_sync_callback(void *data, DBusMessage *msg __UNUSED__, DBusError *err) +{ + E_Bluez_Instance *inst = data; + Eina_Bool powered; + Eina_Bool discoverable; + + if (err && dbus_error_is_set(err)) + { + dbus_error_free(err); + return; + } + + if (!e_bluez_adapter_powered_get(inst->adapter, &powered)) + { + _bluez_operation_error_show(_("Query adapter's powered.")); + return; + } + + inst->powered = powered; + + if (!e_bluez_adapter_discoverable_get(inst->adapter, &discoverable)) + { + _bluez_operation_error_show(_("Query adapter's discoverable.")); + return; + } + + inst->discoverable = discoverable; +} + +static void +_default_adapter_callback(void *data, DBusMessage *msg, DBusError *err __UNUSED__) +{ + E_Bluez_Module_Context *ctxt = data; + const Eina_List *l; + E_Bluez_Instance *inst; + const char *path; + + if (dbus_message_get_args(msg, NULL, DBUS_TYPE_OBJECT_PATH, &path, + DBUS_TYPE_INVALID) == FALSE) + return; + + eina_stringshare_replace(&ctxt->default_adapter, path); + + // TODO: instead of getting the default adapter, get the adapter for + // each instance. See the mixer module. + EINA_LIST_FOREACH(ctxt->instances, l, inst) + { + inst->adapter = e_bluez_adapter_get(path); + + e_bluez_element_properties_sync_full + (inst->adapter, _properties_sync_callback, inst); + } +} + +static int +_bluez_event_manager_in(void *data, int type __UNUSED__, void *event __UNUSED__) +{ + E_Bluez_Module_Context *ctxt = data; + E_Bluez_Element *element; + + ctxt->has_manager = EINA_TRUE; + + element = e_bluez_manager_get(); + if (!e_bluez_manager_default_adapter(_default_adapter_callback, ctxt)) + return 0; + + e_bluez_element_listener_add(element, _bluez_manager_changed, ctxt, NULL); + + return 1; +} + +static int +_bluez_event_manager_out(void *data, int type __UNUSED__, void *event __UNUSED__) +{ + E_Bluez_Module_Context *ctxt = data; + + ctxt->has_manager = EINA_FALSE; + eina_stringshare_replace(&ctxt->default_adapter, NULL); + + return 1; +} + +static int +_bluez_event_element_updated(void *data, int type __UNUSED__, void *event __UNUSED__) +{ + E_Bluez_Module_Context *ctxt = data; + E_Bluez_Element *element = event; + Eina_Bool powered, discoverable, discovering; + E_Bluez_Instance *inst; + Eina_List *l; + + if (!e_bluez_element_is_adapter(element)) return 1; + + if (!e_bluez_adapter_powered_get(element, &powered)) + powered = EINA_FALSE; + + if (!e_bluez_adapter_discoverable_get(element, &discoverable)) + discoverable = EINA_FALSE; + + if (!e_bluez_adapter_discovering_get(element, &discovering)) + discovering = EINA_FALSE; + + EINA_LIST_FOREACH(ctxt->instances, l, inst) + { + if (inst->adapter != element) continue; + + inst->powered = powered; + inst->discoverable = discoverable; + inst->discovering = discovering; + _bluez_gadget_update(inst); + } + + return 1; +} + +static void +_bluez_events_register(E_Bluez_Module_Context *ctxt) +{ + ctxt->event.manager_in = ecore_event_handler_add + (E_BLUEZ_EVENT_MANAGER_IN, _bluez_event_manager_in, ctxt); + ctxt->event.manager_out = ecore_event_handler_add + (E_BLUEZ_EVENT_MANAGER_OUT, _bluez_event_manager_out, ctxt); + ctxt->event.element_updated = ecore_event_handler_add + (E_BLUEZ_EVENT_ELEMENT_UPDATED, _bluez_event_element_updated, ctxt); + ctxt->event.device_found = ecore_event_handler_add + (E_BLUEZ_EVENT_DEVICE_FOUND, _bluez_event_devicefound, ctxt); + + // TODO: E_BLUEZ_EVENT_DEVICE_DISAPPEARED +} + +static void +_bluez_events_unregister(E_Bluez_Module_Context *ctxt) +{ + if (ctxt->event.manager_in) + ecore_event_handler_del(ctxt->event.manager_in); + if (ctxt->event.manager_out) + ecore_event_handler_del(ctxt->event.manager_out); + if (ctxt->event.device_found) + ecore_event_handler_del(ctxt->event.device_found); +} + +static void +_bluez_agent_register(E_Bluez_Module_Context *ctxt) +{ + E_DBus_Object *o; + + ctxt->agent.iface = e_dbus_interface_new("org.bluez.Agent"); + if (!ctxt->agent.iface) + return; + + o = e_dbus_object_add(ctxt->agent.conn, _e_bluez_agent_path, ctxt); + e_dbus_object_interface_attach(o, ctxt->agent.iface); + e_dbus_interface_method_add + (ctxt->agent.iface, "RequestPinCode", "o", "s", _bluez_request_pincode_cb); + // TODO: RequestPasskey + // TODO: RequestConfirmation + // TODO: Authorize + // TODO: DisplayPasskey + // TODO: ConfirmModeChange + // TODO: Cancel + // TODO: Release + + ctxt->agent.obj = o; +} + +static void +_bluez_agent_unregister(E_Bluez_Module_Context *ctxt) +{ + E_Object *o; + + EINA_LIST_FREE(ctxt->agent.pending, o) + e_object_del(o); + + e_dbus_object_interface_detach(ctxt->agent.obj, ctxt->agent.iface); + e_dbus_object_free(ctxt->agent.obj); + e_dbus_interface_unref(ctxt->agent.iface); +} + +EAPI void * +e_modapi_init(E_Module *m) +{ + E_Bluez_Module_Context *ctxt = E_NEW(E_Bluez_Module_Context, 1); + if (!ctxt) + return NULL; + + ctxt->agent.conn = e_dbus_bus_get(DBUS_BUS_SYSTEM); + if (!e_bluez_system_init(ctxt->agent.conn)) + goto error_bluez_system_init; + + bluez_mod = m; + + if (_e_bluez_log_dom < 0) + { + _e_bluez_log_dom = eina_log_domain_register("ebluez", EINA_COLOR_ORANGE); + if (_e_bluez_log_dom < 0) + { + //EINA_LOG_CRIT("could not register logging domain ebluez"); + goto error_log_domain; + } + } + + _bluez_agent_register(ctxt); + _bluez_actions_register(ctxt); + e_gadcon_provider_register(&_gc_class); + + _bluez_events_register(ctxt); + + return ctxt; + + error_log_domain: + _e_bluez_log_dom = -1; + bluez_mod = NULL; + e_bluez_system_shutdown(); + error_bluez_system_init: + E_FREE(ctxt); + return NULL; +} + +static void +_bluez_instances_free(E_Bluez_Module_Context *ctxt) +{ + E_Bluez_Instance *inst; + EINA_LIST_FREE(ctxt->instances, inst) + { + if (inst->popup) + _bluez_popup_del(inst); + if (inst->tip) + _bluez_tip_del(inst); + + e_object_del(E_OBJECT(inst->gcc)); + } +} + +EAPI int +e_modapi_shutdown(E_Module *m) +{ + E_Bluez_Module_Context *ctxt = m->data; + E_Bluez_Element *element; + + if (!ctxt) + return 0; + + element = e_bluez_manager_get(); + e_bluez_element_listener_del(element, _bluez_manager_changed, ctxt); + + _bluez_events_unregister(ctxt); + _bluez_instances_free(ctxt); + + _bluez_actions_unregister(ctxt); + _bluez_agent_unregister(ctxt); + e_gadcon_provider_unregister(&_gc_class); + + if (ctxt->poller.manager_changed) + ecore_poller_del(ctxt->poller.manager_changed); + + eina_stringshare_del(ctxt->default_adapter); + + E_FREE(ctxt); + bluez_mod = NULL; + + e_bluez_system_shutdown(); + + return 1; +} + +EAPI int +e_modapi_save(E_Module *m __UNUSED__) +{ + return 1; +} diff --git a/src/modules/bluez/e_mod_main.h b/src/modules/bluez/e_mod_main.h new file mode 100644 index 000000000..7a3dd7e98 --- /dev/null +++ b/src/modules/bluez/e_mod_main.h @@ -0,0 +1,146 @@ +/* + * vim:ts=8:sw=3:sts=8:noexpandtab:cino=>5n-3f0^-2{2 + */ +#ifndef E_MOD_MAIN_H +#define E_MOD_MAIN_H + +#include "config.h" +#include +#include +#include + +#define MOD_CONF_VERSION 2 + +extern int _e_bluez_log_dom; +#define DBG(...) EINA_LOG_DOM_DBG(_e_bluez_log_dom, __VA_ARGS__) +#define WRN(...) EINA_LOG_DOM_WARN(_e_bluez_log_dom, __VA_ARGS__) +#define ERR(...) EINA_LOG_DOM_ERR(_e_bluez_log_dom, __VA_ARGS__) + +typedef struct E_Bluez_Instance E_Bluez_Instance; +typedef struct E_Bluez_Instance_Device E_Bluez_Instance_Device; +typedef struct E_Bluez_Module_Context E_Bluez_Module_Context; + +struct E_Bluez_Instance +{ + E_Bluez_Module_Context *ctxt; + E_Gadcon_Client *gcc; + E_Gadcon_Popup *popup; + E_Menu *menu; + + /* used by popup */ + int powered; + Eina_Bool first_selection; + const char *address; + const char *alias; + + Eina_List *devices; + E_Bluez_Element *adapter; + double last_scan; + Eina_Bool discovering:1; + Eina_Bool powered_pending:1; + Eina_Bool discoverable:1; + + struct + { + Evas_Object *gadget; + Evas_Object *list; + Evas_Object *powered; + Evas_Object *button; + Evas_Object *control; + struct + { + Ecore_X_Window win; + Ecore_Event_Handler *mouse_up; + Ecore_Event_Handler *key_down; + } input; + } ui; + + E_Gadcon_Popup *tip; + Evas_Object *o_tip; + + E_Config_Dialog *conf_dialog; +}; + +struct E_Bluez_Instance_Device +{ + const char *address; + const char *alias; + // TODO (and also show list icon!): Eina_Bool paired:1; + // TODO (and also show list icon!): Eina_Bool trusted:1; + // TODO (and also show list icon!): Eina_Bool connected:1; + // TODO ... class, icon +}; + +struct E_Bluez_Module_Context +{ + Eina_List *instances; + E_Bluez_Instance *default_instance; + const char *default_adapter; + + struct { + E_DBus_Connection *conn; + E_DBus_Interface *iface; + E_DBus_Object *obj; + Eina_List *pending; + } agent; + + struct + { + E_Action *toggle_powered; + } actions; + + struct + { + Ecore_Event_Handler *manager_in; + Ecore_Event_Handler *manager_out; + Ecore_Event_Handler *device_found; + Ecore_Event_Handler *element_updated; + } event; + + struct + { + Ecore_Poller *manager_changed; + } poller; + + Eina_Bool has_manager:1; +}; + +EAPI extern E_Module_Api e_modapi; +EAPI void *e_modapi_init (E_Module *m); +EAPI int e_modapi_shutdown (E_Module *m); +EAPI int e_modapi_save (E_Module *m); + +const char *e_bluez_theme_path(void); +E_Config_Dialog *e_bluez_config_dialog_new(E_Container *con, E_Bluez_Instance *inst); +void _bluez_toggle_powered(E_Bluez_Instance *inst); + + +static inline void +_bluez_dbus_error_show(const char *msg, const DBusError *error) +{ + const char *name; + + if ((!error) || (!dbus_error_is_set(error))) + return; + + name = error->name; + if (strncmp(name, "org.bluez.Error.", + sizeof("org.bluez.Error.") - 1) == 0) + name += sizeof("org.bluez.Error.") - 1; + + e_util_dialog_show(_("Bluez Server Operation Failed"), + _("Could not execute remote operation:
" + "%s
" + "Server Error %s: %s"), + msg, name, error->message); +} + +static inline void +_bluez_operation_error_show(const char *msg) +{ + e_util_dialog_show(_("Bluez Operation Failed"), + _("Could not execute local operation:
%s"), + msg); +} + +#endif diff --git a/src/modules/bluez/module.desktop.in b/src/modules/bluez/module.desktop.in new file mode 100644 index 000000000..072d5733a --- /dev/null +++ b/src/modules/bluez/module.desktop.in @@ -0,0 +1,6 @@ +[Desktop Entry] +Type=Link +Name=Bluetooth Manager +Icon=e-module-bluez +Comment=Bluetooth Management. +X-Enlightenment-ModuleType=system