Finishing payments.

This commit is contained in:
Sergiotarxz 2024-10-29 19:11:29 +01:00
parent 59e7904721
commit 5dda40ca42
11 changed files with 928 additions and 185 deletions

4
app_config.json Normal file
View File

@ -0,0 +1,4 @@
{
"licenser-server": "https://tnsoehasuhaons.beta.exd.sergiotarxz.me",
"debug": true
}

BIN
exd-logo.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

417
exd.svg
View File

@ -7,10 +7,11 @@
viewBox="0 0 512 512"
version="1.1"
id="svg1"
inkscape:version="1.4 (e7c3feb100, 2024-10-09)"
sodipodi:docname="exd.svg"
sodipodi:docname="exd-final.svg"
inkscape:version="1.3.2 (091e20ef0f, 2023-11-25)"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<sodipodi:namedview
@ -23,85 +24,367 @@
inkscape:pagecheckerboard="0"
inkscape:deskcolor="#d1d1d1"
inkscape:document-units="px"
inkscape:zoom="1.0131836"
inkscape:cx="159.89205"
inkscape:cy="321.75807"
inkscape:window-width="1290"
inkscape:window-height="1011"
inkscape:window-x="20"
inkscape:window-y="20"
inkscape:window-maximized="0"
inkscape:zoom="0.71972656"
inkscape:cx="229.94844"
inkscape:cy="249.40027"
inkscape:window-width="1920"
inkscape:window-height="1003"
inkscape:window-x="0"
inkscape:window-y="0"
inkscape:window-maximized="1"
inkscape:current-layer="layer1" />
<defs
id="defs1">
<rect
x="228.9812"
y="200.35855"
width="94.750843"
height="31.583614"
id="rect4" />
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient54"
id="linearGradient146"
gradientUnits="userSpaceOnUse"
gradientTransform="matrix(14.931319,0,0,15.662846,3.1629448,-6.2364669)"
x1="3.3060381"
y1="26.921986"
x2="30.560637"
y2="26.921986" />
<linearGradient
id="linearGradient54"
inkscape:collect="always">
<stop
style="stop-color:#409172;stop-opacity:1;"
offset="0"
id="stop54" />
<stop
style="stop-color:#50ba95;stop-opacity:1;"
offset="0.03402343"
id="stop59" />
<stop
style="stop-color:#378c6d;stop-opacity:1;"
offset="0.07285479"
id="stop56" />
<stop
style="stop-color:#378c6d;stop-opacity:1;"
offset="0.92714483"
id="stop57" />
<stop
style="stop-color:#50ba95;stop-opacity:1;"
offset="0.96597624"
id="stop58" />
<stop
style="stop-color:#409172;stop-opacity:1;"
offset="1"
id="stop55" />
</linearGradient>
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient4"
id="linearGradient145"
gradientUnits="userSpaceOnUse"
gradientTransform="matrix(14.931319,0,0,15.662846,3.1629448,-6.2364669)"
x1="16.933332"
y1="3.1749997"
x2="16.933332"
y2="29.633331" />
<linearGradient
id="linearGradient4"
inkscape:collect="always">
<stop
style="stop-color:#ffffff;stop-opacity:0.7970323;"
offset="0"
id="stop4" />
<stop
style="stop-color:#ffffff;stop-opacity:0.3975068;"
offset="0.24830961"
id="stop6" />
<stop
style="stop-color:#ffffff;stop-opacity:0;"
offset="0.50635862"
id="stop8" />
<stop
style="stop-color:#378c6d;stop-opacity:0;"
offset="0.57348305"
id="stop9" />
<stop
style="stop-color:#378c6d;stop-opacity:0.54033399;"
offset="0.73684102"
id="stop7" />
<stop
style="stop-color:#1c8c63;stop-opacity:1;"
offset="1"
id="stop5" />
</linearGradient>
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient62"
id="linearGradient144"
gradientUnits="userSpaceOnUse"
x1="10.374425"
y1="17.991667"
x2="10.374561"
y2="19.453598" />
<linearGradient
id="linearGradient62"
inkscape:collect="always">
<stop
style="stop-color:#9a9996;stop-opacity:1"
offset="0"
id="stop63" />
<stop
style="stop-color:#423f46;stop-opacity:1;"
offset="1"
id="stop64" />
</linearGradient>
<clipPath
clipPathUnits="userSpaceOnUse"
id="clipPath29">
<path
id="path30"
style="opacity:1;fill:#9141ac;fill-opacity:1;stroke-width:7.48075;stroke-linecap:round;stroke-linejoin:round;paint-order:markers stroke fill"
d="m 9.5250001,16.557475 v 1.981445 c 0,0.283266 0.2278144,0.51108 0.5110799,0.51108 h 13.794507 c 0.283265,0 0.51108,-0.227814 0.51108,-0.51108 v -1.981445 z"
sodipodi:nodetypes="csssscc" />
</clipPath>
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient59"
id="linearGradient143"
gradientUnits="userSpaceOnUse"
gradientTransform="matrix(14.931319,0,0,15.662846,3.1629448,-6.2364669)"
x1="12.7"
y1="19.049999"
x2="12.7"
y2="27.516665" />
<linearGradient
id="linearGradient59"
inkscape:collect="always">
<stop
style="stop-color:#ffffff;stop-opacity:1;"
offset="0"
id="stop60" />
<stop
style="stop-color:#e8e7e3;stop-opacity:1;"
offset="0.375"
id="stop62" />
<stop
style="stop-color:#deddda;stop-opacity:1"
offset="1"
id="stop61" />
</linearGradient>
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient170"
id="linearGradient165"
x1="14.287501"
y1="87.312508"
x2="14.287501"
y2="85.195839"
gradientUnits="userSpaceOnUse"
gradientTransform="matrix(14.931319,0,0,15.662846,3.1629448,-1122.7502)" />
<linearGradient
id="linearGradient170"
inkscape:collect="always">
<stop
style="stop-color:#338265;stop-opacity:1;"
offset="0"
id="stop169" />
<stop
style="stop-color:#5dbd9a;stop-opacity:1;"
offset="1"
id="stop170" />
</linearGradient>
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient170"
id="linearGradient167"
x1="15.875001"
y1="87.312508"
x2="15.875001"
y2="85.195839"
gradientUnits="userSpaceOnUse"
gradientTransform="matrix(14.931319,0,0,15.662846,1.118254,-1122.7502)" />
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient170"
id="linearGradient169"
x1="17.991669"
y1="87.312508"
x2="17.991669"
y2="85.195839"
gradientUnits="userSpaceOnUse"
gradientTransform="matrix(14.931319,0,0,15.662846,5.2076546,-1122.7502)" />
</defs>
<g
inkscape:label="Layer 1"
inkscape:groupmode="layer"
id="layer1">
<rect
style="fill:url(#linearGradient146);fill-opacity:1;stroke-width:66.0493;stroke-linecap:round;stroke-linejoin:round;paint-order:markers stroke fill"
id="rect129"
width="406.94711"
height="113.98333"
x="52.526443"
y="358.44681"
ry="32.772198" />
<rect
style="fill:#75dfb9;fill-opacity:1;stroke-width:66.0493;stroke-linecap:round;stroke-linejoin:round;paint-order:markers stroke fill"
id="rect130"
width="406.94711"
height="394.11246"
x="52.526443"
y="45.545456"
ry="32.772198" />
<rect
style="opacity:0.962206;fill:url(#linearGradient145);fill-opacity:0.511007;stroke-width:66.0493;stroke-linecap:round;stroke-linejoin:round;paint-order:markers stroke fill"
id="rect131"
width="406.94711"
height="394.11246"
x="52.526443"
y="45.545456"
ry="32.772198" />
<rect
style="fill:#75dfb9;fill-opacity:1;stroke-width:58.5262;stroke-linecap:round;stroke-linejoin:round;paint-order:markers stroke fill"
id="rect132"
width="360.59457"
height="349.22183"
x="75.702713"
y="77.017487"
ry="17.691614" />
<path
style="fill:#75dfb9;fill-opacity:1;stroke:none;stroke-width:15.6084"
d="M 418.19601,434.27472 108.73222,432.7412 C 87.30367,428.80108 66.39357,423.98239 61.80804,391.50124 L 62.19265,100.62701 C 61.50823,72.24757 76.96556,65.23751 89.35122,54.16097 l 323.90923,1.27354 c 23.94476,4.70727 33.02901,22.00536 39.99971,41.09419 l 3.06755,295.21917 c -3.23996,19.90715 -11.02317,37.06474 -38.1317,42.52685 z"
id="path1"
sodipodi:nodetypes="ccccccccc" />
id="path132"
style="fill:#378c6d;fill-opacity:1;stroke-width:114.401;stroke-linecap:round;stroke-linejoin:round;paint-order:markers stroke fill"
d="m 161.18607,272.63285 -7.90117,8.87913 -5.7407,-6.45092 c -1.33434,1.44066 -2.16044,3.4001 -2.16044,5.57675 v 16.74012 c 0,4.43679 3.40156,8.00497 7.6311,8.00497 h 205.97018 c 4.22954,0 7.63109,-3.56818 7.63109,-8.00497 v -16.74012 c 0,-2.17665 -0.82615,-4.13609 -2.16049,-5.57675 l -5.74067,6.45092 -7.90114,-8.87913 -7.90116,8.87913 -7.90115,-8.87913 -7.90117,8.87913 -7.90114,-8.87913 -7.90117,8.87913 -7.90115,-8.87913 -7.90116,8.87913 -7.90115,-8.87913 -7.90116,8.87913 -7.90115,-8.87913 -7.90117,8.87913 -7.90114,-8.87913 -7.90117,8.87913 -7.90115,-8.87913 -7.90116,8.87913 -7.90115,-8.87913 -7.90117,8.87913 -7.90114,-8.87913 -7.90117,8.87913 -7.90114,-8.87913 -7.90117,8.87913 -7.90115,-8.87913 -7.90116,8.87913 z"
sodipodi:nodetypes="cccssssssccccccccccccccccccccccccccc" />
<path
style="fill:#d9d3d0;fill-opacity:1;stroke:#2b0000;stroke-width:7.95089;stroke-dasharray:none;stroke-opacity:1"
d="m 363.7621,284.44678 v -28.39463 l -207.73101,-2.98891 4.50879,27.40765 z"
id="path3"
sodipodi:nodetypes="ccccc" />
id="path133"
style="fill:#136246;fill-opacity:1;stroke-width:114.401;stroke-linecap:round;stroke-linejoin:round;paint-order:markers stroke fill"
d="m 161.18607,276.74258 -7.90117,8.87912 -5.7407,-6.45091 c -1.33434,1.44067 -2.16044,3.40014 -2.16044,5.57676 v 15.96472 c 0,4.43673 3.40156,8.00496 7.6311,8.00496 h 205.97018 c 4.22954,0 7.63109,-3.56823 7.63109,-8.00496 v -15.96472 c 0,-2.17662 -0.82615,-4.13609 -2.16049,-5.57676 l -5.74067,6.45091 -7.90114,-8.87912 -7.90116,8.87912 -7.90115,-8.87912 -7.90117,8.87912 -7.90114,-8.87912 -7.90117,8.87912 -7.90115,-8.87912 -7.90116,8.87912 -7.90115,-8.87912 -7.90116,8.87912 -7.90115,-8.87912 -7.90117,8.87912 -7.90114,-8.87912 -7.90117,8.87912 -7.90115,-8.87912 -7.90116,8.87912 -7.90115,-8.87912 -7.90117,8.87912 -7.90114,-8.87912 -7.90117,8.87912 -7.90114,-8.87912 -7.90117,8.87912 -7.90115,-8.87912 -7.90116,8.87912 z"
sodipodi:nodetypes="cccssssssccccccccccccccccccccccccccc" />
<path
style="fill:#371600;fill-opacity:1;stroke:#2b0000;stroke-width:7.95089;stroke-dasharray:none;stroke-opacity:1"
d="m 356.69643,305.36917 -206.31788,4.48336 1.41313,-46.32812 25.43646,13.45011 12.71821,-11.95565 32.50213,16.43901 16.95764,-13.45009 18.37078,13.45009 25.43643,-13.45009 29.67587,16.43899 22.61019,-13.45009 21.19704,13.45009 z"
id="path2" />
style="fill:url(#linearGradient144);fill-opacity:1;stroke:none;stroke-width:0.182682;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;paint-order:markers stroke fill"
d="m 9.5988037,20.108333 c 0,-1.286464 0.3197629,-2.116666 0.7756213,-2.116666 h 1.513594 v 2.116666 z"
id="path134"
sodipodi:nodetypes="ccccc"
inkscape:transform-center-x="1.4581046"
inkscape:transform-center-y="-4.3858528"
clip-path="url(#clipPath29)"
transform="matrix(14.931319,0,0,15.662846,3.1629451,10.340015)" />
<path
style="fill:#fbfbec;fill-opacity:1;stroke:#2b0000;stroke-width:7.95089;stroke-dasharray:none;stroke-opacity:1"
d="m 336.95667,291.41161 c 39.95551,-15.89369 34.57135,66.53441 6.88683,93.24751 l -193.2618,-4.907 c 34.80282,-25.57481 50.68048,-107.3464 15.36715,-73.3849 -4.48827,4.31645 -7.28273,-10.99003 -11.75705,-17.35321 z"
id="path4"
sodipodi:nodetypes="cccscc" />
id="path135"
style="fill:url(#linearGradient143);fill-opacity:1;stroke:none;stroke-width:2.79371;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;paint-order:markers stroke fill"
inkscape:transform-center-x="-11.49783"
inkscape:transform-center-y="-2.1929348"
d="m 158.06883,292.14071 c 6.80652,0 11.01839,13.19126 11.01839,33.15303 v 99.45902 l 7.90116,-8.2882 7.90115,8.2882 7.90118,-8.2882 7.90114,8.2882 7.90117,-8.2882 7.90114,8.2882 7.90117,-8.2882 7.90115,8.2882 7.90116,-8.2882 7.90115,8.2882 7.90117,-8.2882 7.90114,8.2882 7.90117,-8.2882 7.90115,8.2882 7.90116,-8.2882 7.90114,8.2882 7.90116,-8.2882 7.90115,8.2882 7.90117,-8.2882 7.90114,8.2882 7.90117,-8.2882 7.90115,8.2882 7.90116,-8.2882 7.90115,8.2882 7.90116,-8.2882 7.90115,8.2882 v -99.45902 c 0,-19.96177 -4.21186,-33.15303 -11.0184,-33.15303 z"
sodipodi:nodetypes="cscccccccccccccccccccccccccccssc" />
<path
id="path136"
style="fill:#bab8b6;fill-opacity:1;stroke:none;stroke-width:2.79371;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;paint-order:markers stroke fill"
inkscape:transform-center-x="-11.49783"
inkscape:transform-center-y="-2.1929298"
d="m 181.64114,300.42901 c 0.83465,2.33144 1.5129,5.12621 2.037,8.28826 h 86.91272 c -0.52409,-3.16205 -1.20245,-5.95682 -2.03705,-8.28826 z m 126.41847,0 c 0.83467,2.33144 1.51293,5.12621 2.03702,8.28826 h 7.90116 c -0.52408,-3.16205 -1.20244,-5.95682 -2.03703,-8.28826 z m 15.8023,0 c 0.83468,2.33144 1.51295,5.12621 2.03704,8.28826 h 31.78981 c -0.52411,-3.16205 -1.20241,-5.95682 -2.03702,-8.28826 z m -139.25013,16.57651 c 0.17652,2.60968 0.27771,5.36511 0.27771,8.28826 h 86.91273 c 0,-2.92315 -0.10161,-5.67858 -0.27773,-8.28826 z m 126.41848,0 c 0.17615,2.60968 0.27773,5.36511 0.27773,8.28826 h 7.90117 c 0,-2.92315 -0.10161,-5.67858 -0.27773,-8.28826 z m 15.80231,0 c 0.17615,2.60968 0.27774,5.36511 0.27774,8.28826 h 31.78978 c 0,-2.92315 -0.10161,-5.67858 -0.27771,-8.28826 z" />
<text
xml:space="preserve"
id="text4"
style="text-align:start;writing-mode:lr-tb;direction:ltr;white-space:pre;shape-inside:url(#rect4);display:inline;fill:#5acea0;fill-opacity:1;stroke:none;stroke-width:5.4;stroke-dasharray:none;stroke-opacity:1"
transform="matrix(1.4317665,0,0,1.5141573,-73.468577,22.490569)"><tspan
x="228.98047"
y="224.08681"
id="tspan2"><tspan
style="font-size:26.6667px;font-family:'Liberation Mono';-inkscape-font-specification:'Liberation Mono, Normal'"
id="tspan1">EXD</tspan></tspan></text>
<ellipse
style="fill:#1bc9a2;fill-opacity:1;stroke:none;stroke-width:5.4;stroke-dasharray:none;stroke-opacity:1"
id="path7"
cx="-153.44371"
cy="-179.50844"
rx="39.973011"
ry="29.609638"
transform="scale(-1)" />
<ellipse
style="fill:#beffe1;fill-opacity:1;stroke:none;stroke-width:3.7116;stroke-dasharray:none;stroke-opacity:1"
id="path7-2"
cx="-247.51122"
cy="-211.81203"
rx="27.635662"
ry="20.233253"
transform="scale(-1)" />
<ellipse
style="fill:#1bc9a2;fill-opacity:1;stroke:none;stroke-width:5.4;stroke-dasharray:none;stroke-opacity:1"
id="path7-7"
cx="-346.70349"
cy="-174.8"
rx="39.973011"
ry="29.609638"
transform="scale(-1)" />
style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:66.4819px;font-family:Cantarell;-inkscape-font-specification:'Cantarell Bold';text-align:justify;opacity:0.349358;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.66206;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;paint-order:markers stroke fill"
x="240.62625"
y="390.69879"
id="text136"
transform="scale(0.97636847,1.0242035)"><tspan
sodipodi:role="line"
id="tspan136"
style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-family:Cantarell;-inkscape-font-specification:'Cantarell Bold';fill:#000000;stroke-width:1.66206"
x="240.62625"
y="390.69879">EXD</tspan></text>
<path
id="rect7"
style="fill:none;stroke:#2b0000;stroke-width:4.7838"
d="m 430.82675,373.76224 c -5.29241,24.3079 -20.35963,34.63148 -42.46611,34.88433 L 116.19612,408.06161 C 96.34024,401.6588 85.80851,388.5862 82.77241,370.15178 l 2.96251,-260.9134 c 4.8641,-15.18114 6.5647,-33.178351 32.18836,-29.87971 l 275.94999,-2.70784 c 20.77162,8.07608 33.36126,18.37634 35.15087,43.40105 z"
sodipodi:nodetypes="ccccccccc" />
style="opacity:0.962206;fill:#959492;fill-opacity:1;stroke:none;stroke-width:15.5961;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1;paint-order:markers stroke fill"
d="m 319.2092,333.58203 v 8.28825 h 39.50577 v -8.28825 z"
id="path137" />
<path
style="opacity:0.962206;fill:#bab8b6;fill-opacity:1;stroke:none;stroke-width:15.5961;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1;paint-order:markers stroke fill"
d="m 303.40688,333.58203 v 8.28825 h 7.90115 v -8.28825 z"
id="path138" />
<path
style="opacity:0.962206;fill:#bab8b6;fill-opacity:1;stroke:none;stroke-width:15.5961;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1;paint-order:markers stroke fill"
d="m 184.88953,333.58203 v 8.28825 h 39.50579 v -8.28825 z"
id="path139" />
<path
style="fill:none;fill-opacity:1;stroke:#338265;stroke-width:16.1847;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1;paint-order:markers stroke fill"
id="path140"
sodipodi:type="arc"
sodipodi:cx="182.85791"
sodipodi:cy="194.53314"
sodipodi:rx="39.532063"
sodipodi:ry="41.468849"
sodipodi:start="3.4870691"
sodipodi:end="5.9389792"
sodipodi:arc-type="arc"
d="m 145.66163,180.48992 a 39.532063,41.468849 0 0 1 37.22139,-27.42562 39.532063,41.468849 0 0 1 37.18815,27.4752"
sodipodi:open="true" />
<path
style="fill:none;fill-opacity:1;stroke:#338265;stroke-width:16.1847;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1;paint-order:markers stroke fill"
id="path141"
sodipodi:type="arc"
sodipodi:cx="334.94324"
sodipodi:cy="194.53314"
sodipodi:rx="39.532063"
sodipodi:ry="41.468849"
sodipodi:start="3.4870691"
sodipodi:end="5.9389792"
sodipodi:arc-type="arc"
d="m 297.74696,180.48992 a 39.532063,41.468849 0 0 1 37.22139,-27.42562 39.532063,41.468849 0 0 1 37.18814,27.4752"
sodipodi:open="true" />
<path
style="fill:none;fill-opacity:1;stroke:#136246;stroke-width:16.1847;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1;paint-order:markers stroke fill"
id="path142"
sodipodi:type="arc"
sodipodi:cx="182.85791"
sodipodi:cy="194.53314"
sodipodi:rx="39.532063"
sodipodi:ry="41.468849"
sodipodi:start="3.4870691"
sodipodi:end="5.9389792"
sodipodi:arc-type="arc"
d="m 145.66163,180.48992 a 39.532063,41.468849 0 0 1 37.22139,-27.42562 39.532063,41.468849 0 0 1 37.18815,27.4752"
sodipodi:open="true" />
<path
style="fill:none;fill-opacity:1;stroke:#136246;stroke-width:16.1847;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1;paint-order:markers stroke fill"
id="path143"
sodipodi:type="arc"
sodipodi:cx="334.94324"
sodipodi:cy="194.53314"
sodipodi:rx="39.532063"
sodipodi:ry="41.468849"
sodipodi:start="3.4870691"
sodipodi:end="5.9389792"
sodipodi:arc-type="arc"
d="m 297.74696,180.48992 a 39.532063,41.468849 0 0 1 37.22139,-27.42562 39.532063,41.468849 0 0 1 37.18814,27.4752"
sodipodi:open="true" />
<g
id="rect170"
transform="matrix(14.931319,0,0,15.662846,3.1629451,-1124.2224)">
<path
id="path171"
style="color:#000000;fill:#136246;fill-opacity:1;stroke-linecap:round;stroke-linejoin:round;-inkscape-stroke:none;paint-order:markers stroke fill"
d="m 13.226583,85.460934 c -0.655338,0 -1.187524,0.532187 -1.187524,1.187525 v 0.0057 c 0,0.655338 0.532186,1.187525 1.187524,1.187525 h 7.414018 c 0.655338,0 1.187524,-0.532187 1.187524,-1.187525 v -0.0057 c 0,-0.655338 -0.532186,-1.187525 -1.187524,-1.187525 z" />
</g>
<rect
style="fill:#75dfb9;fill-opacity:1;stroke:url(#linearGradient165);stroke-width:4.0462;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;paint-order:markers stroke fill"
id="rect156"
width="142.22083"
height="33.153057"
x="184.8895"
y="211.65942"
ry="16.520575" />
<rect
style="fill:url(#linearGradient167);stroke:none;stroke-width:4.0462;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1;paint-order:markers stroke fill"
id="rect160"
width="4.089407"
height="33.153057"
x="228.20706"
y="211.65942"
ry="0" />
<rect
style="fill:url(#linearGradient169);stroke:none;stroke-width:4.0462;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1;paint-order:markers stroke fill"
id="rect161"
width="4.089407"
height="33.153057"
x="279.70337"
y="211.65942"
ry="0" />
</g>
</svg>

Before

Width:  |  Height:  |  Size: 4.6 KiB

After

Width:  |  Height:  |  Size: 18 KiB

72
lib/Exd.pm Normal file
View File

@ -0,0 +1,72 @@
package Exd;
use v5.40.0;
use strict;
use warnings;
use utf8;
use Moo;
use JSON;
use Path::Tiny;
use UUID::URandom qw/create_uuid_string/;
has config => ( is => 'lazy' );
sub _build_config($self) {
return JSON::from_json( $self->_config_file_name->slurp_utf8 );
}
sub _config_file_name($self) {
return path(__FILE__)->parent->parent->child('app_config.json');
}
sub licenser_server($self) {
return $self->config->{'licenser-server'};
}
sub debug($self) {
return $self->config->{debug};
}
sub uuid($self) {
require Exd::DB;
my $dbh = Exd::DB->connect;
my $key = 'licenser-uuid';
my $option =
$dbh->selectrow_hashref( 'SELECT value FROM options WHERE name = ?;',
{}, $key );
my $uuid;
if (defined $option) {
$uuid = $option->{value};
}
if ( !defined $uuid ) {
$uuid = create_uuid_string();
$dbh->do( 'INSERT OR IGNORE INTO options (name, value) VALUES (?, ?);',
{}, $key, $uuid );
}
return $uuid;
}
{
my $key = 'activated';
sub is_activated_license($self) {
require Exd::DB;
my $dbh = Exd::DB->connect;
my $option =
$dbh->selectrow_hashref( 'SELECT value FROM options WHERE name = ?;',
{}, $key );
if (!defined $option) {
return 0;
}
return $option->{value};
}
sub activate_license($self) {
require Exd::DB;
my $dbh = Exd::DB->connect;
$dbh->do( 'INSERT OR IGNORE INTO options (name, value) VALUES (?, ?);',
{}, $key, 1 );
}
}
1;

View File

@ -56,7 +56,6 @@ sub _build__gatt($self) {
sub try_to_connect($self) {
my $device_api = $self->_device;
say $device_api->Connected;
return if $device_api->Connected;
$device_api->Connect;

View File

@ -15,6 +15,7 @@ use Glib::IO;
use Glib::Object::Introspection;
use Path::Tiny;
use GD::Image;
use Mojo::UserAgent;
use IO::Select;
use Capture::Tiny qw/capture/;
@ -25,6 +26,7 @@ use Exd::FileFormat;
use Exd::Gui::PrinterConfigure;
use Exd::Gui::FontGallery;
use Exd::Gui::Instance;
use Exd;
use JSON;
use Net::DBus;
@ -53,6 +55,12 @@ Glib::Object::Introspection->setup(
package => 'Gtk4::Source',
);
Glib::Object::Introspection->setup(
basename => 'Adw',
version => '1',
package => 'Adw',
);
has _app => ( is => 'rw', );
has _select => ( is => 'lazy', );
@ -74,9 +82,27 @@ has _first_time => ( is => 'rw', default => sub { 1 } );
has _last_instance_id => ( is => 'rw', default => sub { 0 } );
has parent_pid => ( is => 'rw' );
has instances => ( is => 'rw', default => sub { return {} } );
has _gresources_path => ( is => 'lazy', );
has _exd => ( is => 'lazy' );
sub start( $self ) {
my $app = Gtk4::Application->new( 'me.sergiotarxz.Exd', [qw/default-flags handles-command-line handles-open/] );
sub _build__gresources_path($self) {
my $root = path(__FILE__)->parent->parent->parent;
my $gresources = $root->child('resources.gresource');
0 == system( 'which', 'glib-compile-resources' )
&& system( 'glib-compile-resources', $root->child('resources.xml') );
if ( !-e $gresources ) {
{
die 'No gresources';
}
}
return $gresources;
}
sub start($self) {
Glib::IO::resources_register(
Glib::IO::Resource::load( $self->_gresources_path ) );
my $app = Adw::Application->new( 'me.sergiotarxz.Exd',
[qw/default-flags handles-command-line handles-open/] );
my $bus = Net::DBus->session;
my $started = 0;
eval {
@ -87,7 +113,7 @@ sub start( $self ) {
$self->_app($app);
my $arguments = [];
$app->signal_connect(
command_line => sub($app, $command_line) {
command_line => sub( $app, $command_line ) {
$arguments = $command_line->get_arguments;
my $exd_file = shift @$arguments;
$self->_activate($exd_file);
@ -105,11 +131,9 @@ sub start( $self ) {
}
);
say @ARGV;
$app->run(\@ARGV);
$app->run( \@ARGV );
}
sub _startup($self) {
my $app = $self->_app;
}
@ -120,6 +144,15 @@ sub send_packet_to_daemon( $self, $instance, $packet ) {
$self->_write_to_script->flush;
}
sub send_packet_check_if_activated( $self, $instance, $uuid) {
$self->send_packet_to_daemon($instance, {
type => 'check-activated',
data => {
uuid => $uuid,
}
});
}
sub _daemon_runner($self) {
my ( $read_from_script, $write_to_parent );
my ( $read_from_parent, $write_to_script );
@ -165,12 +198,51 @@ sub _daemon_runner($self) {
if ( $packet->{type} eq 'font-scan' ) {
next;
}
if ( $packet->{type} eq 'check-activated') {
$self->_on_check_activated($instance_id, $packet->{data});
next;
}
warn 'Packet not recognized: '. Data::Dumper::Dumper $packet;
}
exit;
}
close $read_from_parent;
}
sub _build__exd($self) {
require Exd;
return Exd->new;
}
sub _on_check_activated($self, $instance_id, $data) {
my $pid = fork;
if (!$pid) {
while (1) {
eval {
my $url = $self->_exd->licenser_server.'/get_paid/'.$self->_exd->uuid;
my $ua = Mojo::UserAgent->new;
my $activated = $ua->get($url)->result->json;
if ($activated) {
say 'Program sucessfully activated, thank you!';
$self->_exd->activate_license;
$self->send_packet_to_window($instance_id, {
type => 'activated!',
});
exit;
}
};
if ($@) {
warn $@;
}
sleep 5;
if (!kill 0, $self->parent_pid) {
exit;
}
}
exit;
}
}
sub send_packet_to_window( $self, $instance_id, $packet ) {
$packet->{instance_id} = $instance_id;
$self->_write_to_parent->say( JSON::to_json($packet) );
@ -196,8 +268,8 @@ sub _on_save_packet( $self, $instance_id, $json ) {
$self->_save( $instance_id, $exd, $dest_file );
};
if ($@) {
$self->_log($instance_id, $@);
$self->_send_safe_to_exit($instance_id, 1);
$self->_log( $instance_id, $@ );
$self->_send_safe_to_exit( $instance_id, 1 );
}
exit;
}
@ -222,7 +294,7 @@ sub _on_run_script_packet( $self, $instance_id, $json ) {
$json->@{ 'exd_dir', 'device', 'verbose' };
$device = $self->device_hash_to_object($device);
my ($read, $write);
my ( $read, $write );
pipe $read, $write;
my $new_pid = fork;
@ -239,12 +311,12 @@ sub _on_run_script_packet( $self, $instance_id, $json ) {
close $write;
if ($verbose) {
my $log_getter_pid = fork;
if (!$log_getter_pid) {
$self->_log($instance_id, '### STARTED EXECUTION ###');
while (my $line = <$read>) {
$self->_log($instance_id, $line);
if ( !$log_getter_pid ) {
$self->_log( $instance_id, '### STARTED EXECUTION ###' );
while ( my $line = <$read> ) {
$self->_log( $instance_id, $line );
}
$self->_log($instance_id, '### TERMINATED EXECUTION ###');
$self->_log( $instance_id, '### TERMINATED EXECUTION ###' );
exit;
}
}
@ -353,12 +425,13 @@ sub _log( $self, $instance_id, $message ) {
}
);
}
sub _save( $self, $instance_id, $exd, $dest_file ) {
$self->_log( $instance_id, "Pending save to $dest_file." );
$self->_send_safe_to_exit($instance_id, 0);
$self->_send_safe_to_exit( $instance_id, 0 );
my $zip = $exd->to_zip;
$zip->writeToFileNamed( '' . $dest_file );
$self->_send_safe_to_exit($instance_id, 1);
$self->_send_safe_to_exit( $instance_id, 1 );
$self->_log( $instance_id, "Save completed to $dest_file." );
}

View File

@ -12,13 +12,18 @@ use Glib;
use Glib::IO;
use Glib::Object::Introspection;
use Path::Tiny;
use Mojo::UserAgent;
has app => ( is => 'rw', required => 1, weak_ref => 1 );
has instance_id => ( is => 'rw', required => 1 );
has window => ( is => 'rw' );
has _editor => ( is => 'rw', );
has _safe_to_exit => ( is => 'rw', default => sub { 1 } );
has _transparent_avoid_input => ( is => 'rw' );
has _paywall => ( is => 'rw' );
has device => ( is => 'rw' );
has _pay_url => ( is => 'lazy' );
has _activated => ( is => 'rw' );
has file_format => (
is => 'rw',
default => sub {
@ -47,6 +52,12 @@ has _can_open_printer_configure => ( is => 'rw', default => sub { 1 } );
has _preview_number => ( is => 'rw', default => sub { 0 } );
has _save_path => ( is => 'rw' );
has _want_to_close => ( is => 'rw' );
has _exd => ( is => 'lazy' );
has _overlay => ( is => 'rw' );
sub _build__exd($self) {
return Exd->new;
}
sub start( $self, $exd_file ) {
my $win = $self->app->create_application_window;
@ -116,7 +127,7 @@ sub start( $self, $exd_file ) {
$execute_log->set_editable(0);
$execute_log->set_cursor_visible(0);
$win->set_title('Exd (Thermal Printer)');
$win->set_title('Hiperthermia (Thermal Printer) ' . ($self->_exd->debug ? 'DEBUG' : '') );
$win->set_default_size( 1200, 900 );
$execute_log->set_hexpand(1);
@ -150,20 +161,192 @@ sub start( $self, $exd_file ) {
);
$execute_log_window->set_child($execute_log);
$box_vertical->append($execute_log_window);
$win->set_child($box_vertical);
my $overlay = Gtk4::Overlay->new;
$self->_overlay($overlay);
$overlay->set_child($box_vertical);
$self->_activated( $self->_exd->is_activated_license );
if ( !$self->_activated ) {
my $uuid = $self->_exd->uuid;
my $instance_id = $self->instance_id;
$self->_add_timeout_paywall($overlay);
$self->app->send_packet_check_if_activated( $self, $uuid );
}
$win->set_child($overlay);
$win->present;
}
sub _show_about_dialog($self) {
my $window = Gtk4::Window->new;
$window->set_default_size( 650, 650 );
$window->set_transient_for( $self->window );
$window->set_title('About Hiperthermia');
my $picture = Gtk4::Picture->new;
$picture->set_property('width-request', 256);
$picture->set_property('height-request', 256);
$picture->set_filename(path(__FILE__)->parent->parent->parent->parent->child('exd-logo.png').'');
my $label = Gtk4::Label->new(undef);
$label->set_markup(<<"EOF");
Welcome to Hiperthermia.
This is a paid program but fully open source for everyone,
have a copy of the source code at <a href="https://git.owlcode.tech/sergiotarxz/Exd">our source repo</a> under the AGPLv3 license.
NO WARRANTY TO THE EXTENT ALLOWED BY LAW, but fell free to drop
\@sergiotarxz a message if you find bugs, issues or need support
to use this program.
Our amazing contributors:
Sergiotarxz (Developer):
<a href="https://git.owlcode.tech/sergiotarxz">Gitea</a>
\@sergiotarxz in Telegram.
\@sergiotarxz\@owlcode.tech in XMPP.
(Open to freelance work and full time employement,
accepts ETH, BTC and Bank transfers)
Lucidraws (Logo creator):
<a href="https://linktr.ee/lucidraws">lintr.ee</a>
(Open to graphic design comissions,
accepts ETH, BTC and Mercado Pago payments)
EOF
my $box = Gtk4::Box->new( 'vertical', 10 );
$box->set_hexpand(1);
$box->set_vexpand(1);
$box->set_halign('center');
$box->set_valign('center');
$box->append($picture);
$box->append($label);
my $scroll = Gtk4::ScrolledWindow->new;
$scroll->set_vexpand(1);
$scroll->set_hexpand(1);
$scroll->set_child($box);
$window->set_child($scroll);
$window->present;
}
sub _add_timeout_paywall( $self, $overlay ) {
if ( $self->_activated ) {
return 0;
}
if ( defined $self->_pay_url ) {
Glib::Timeout->add(
$self->_exd->debug ? 1_000 : 20_000,
sub {
my $box = Gtk4::Box->new( 'vertical', 10 );
$self->_transparent_avoid_input($box);
$box->set_opacity(0.7);
$box->add_css_class('avoid-main-input');
$box->set_halign('fill');
$box->set_valign('fill');
my $paywall = Gtk4::Box->new( 'vertical', 10 );
$self->_paywall($paywall);
$paywall->set_property( 'width-request', 800 );
$paywall->set_property( 'height-request', 800 );
$paywall->add_css_class('paywall');
my $inner_paywall_box = Gtk4::Box->new( 'vertical', 10 );
my $title = Gtk4::Label->new('This program is not activated');
my $activate = Gtk4::Button->new_with_label('Pay and activate');
my $about = Gtk4::Button->new_with_label('More about the program');
my $remind_me_later =
Gtk4::Button->new_with_label('Remind me later');
$about->signal_connect(
clicked => sub {
$self->_show_about_dialog;
}
);
$activate->signal_connect(
clicked => sub {
my $launcher =
Gtk4::UriLauncher->new( $self->_pay_url );
$launcher->launch( $self->window, undef, undef );
}
);
$remind_me_later->signal_connect(
'clicked' => sub {
$overlay->remove_overlay($paywall);
$overlay->remove_overlay($box);
$self->_paywall(undef);
$self->_transparent_avoid_input(undef);
$self->_add_timeout_paywall($overlay);
}
);
my $picture = Gtk4::Image->new_from_file(path(__FILE__)->parent->parent->parent->parent->child('exd-logo.png').'');
$picture->set_pixel_size(256);
$inner_paywall_box->append($picture);
$inner_paywall_box->append($title);
if ($self->_exd->debug) {
my $debug_message = Gtk4::Label->new('This is debug compilation, do not use your real payment data but a Stripe test card');
$inner_paywall_box->append($debug_message);
}
$inner_paywall_box->append($activate);
$inner_paywall_box->append($remind_me_later);
$inner_paywall_box->append($about);
$inner_paywall_box->set_valign('center');
$inner_paywall_box->set_vexpand(1);
$paywall->set_vexpand(1);
$inner_paywall_box->set_halign('center');
$paywall->set_halign('center');
$paywall->set_valign('fill');
$overlay->add_overlay($box);
$overlay->add_overlay($paywall);
$paywall->append($inner_paywall_box);
Glib::Timeout->add(
1_000,
sub {
return 0 if !defined $self->_transparent_avoid_input;
if ($self->_activated) {
if ( defined $self->_paywall ) {
$self->_overlay->remove_overlay( $self->_paywall );
}
if ( defined $self->_transparent_avoid_input ) {
$self->_overlay->remove_overlay(
$self->_transparent_avoid_input );
}
return 0;
}
return 1;
}
);
return 0;
}
);
}
}
sub _build__pay_url($self) {
my $url;
eval {
my $ua = Mojo::UserAgent->new;
$url = $ua->get(
$self->_exd->licenser_server . '/get_pay_url/' . $self->_exd->uuid )
->result->json->{url};
};
if ($@) {
warn $@;
}
return $url;
}
sub _create_popover_menu( $self, $box ) {
my $menu_model = Glib::IO::Menu->new;
my $file_menu = Glib::IO::Menu->new;
my $help_menu = Glib::IO::Menu->new;
my $open_action =
Glib::IO::SimpleAction->new( 'open.' . $self->instance_id, undef );
my $save_action =
Glib::IO::SimpleAction->new( 'save.' . $self->instance_id, undef );
my $save_as_action =
Glib::IO::SimpleAction->new( 'save-as.' . $self->instance_id, undef );
my $about_action =
Glib::IO::SimpleAction->new( 'about.' . $self->instance_id, undef );
my $app = $self->app->_app;
$about_action->signal_connect(
'activate',
sub {
$self->_show_about_dialog;
}
);
$open_action->signal_connect(
'activate',
sub {
@ -185,10 +368,13 @@ sub _create_popover_menu( $self, $box ) {
$app->add_action($open_action);
$app->add_action($save_action);
$app->add_action($save_as_action);
$app->add_action($about_action);
$file_menu->append( 'Open', 'app.open.' . $self->instance_id );
$file_menu->append( 'Save', 'app.save.' . $self->instance_id );
$file_menu->append( 'Save as', 'app.save-as.' . $self->instance_id );
$help_menu->append( 'About', 'app.about.' . $self->instance_id);
$menu_model->append_submenu( 'File', $file_menu );
$menu_model->append_submenu( 'Help', $help_menu );
my $popover = Gtk4::PopoverMenuBar->new_from_model($menu_model);
$box->append($popover);
}
@ -243,7 +429,7 @@ sub _populate_editor_and_preview( $self, $box_vertical ) {
sub _update_editor_buffer($self) {
my $editor = $self->_editor;
if (defined $editor) {
if ( defined $editor ) {
my $buffer = $editor->get_buffer();
$buffer->set_text( $self->file_format->get_script, -1 );
}
@ -324,6 +510,15 @@ sub receive_packet( $self, $packet ) {
}
return;
}
if ( $packet->{type} eq 'activated!' ) {
$self->mark_activated;
return;
}
warn 'Packet not recognized ' . Data::Dumper::Dumper $packet;
}
sub mark_activated($self) {
$self->_activated(1);
}
sub _add_to_log( $self, $text ) {
@ -562,7 +757,7 @@ sub _open_file( $self, $file ) {
$self->_save_path($file);
my $window = $self->window;
$window->set_title(
"Exd (Thermal Printer) " . path( $self->_save_path )->basename );
"Hiperthermia (Thermal Printer) " . ($self->_exd->debug ? 'DEBUG' : '') . path( $self->_save_path )->basename );
$self->file_format( Exd::FileFormat->from_zip_file($file) );
$self->_update_editor_buffer;
}

View File

@ -17,17 +17,17 @@ use Exd::DeviceToBluetooth;
use Exd::DeviceToRawFile;
use Exd::DeviceToCatPrinter;
has app => (is => 'ro', required => 1);
has instance => (is => 'ro', required => 1);
has app => ( is => 'ro', required => 1 );
has instance => ( is => 'ro', required => 1 );
has on_close => ( is => 'ro', required => 1 );
has _pid_daemon => (is => 'rw');
has _read_from_daemon => (is => 'rw');
has _write_to_app => (is => 'rw');
has _pid_daemon => ( is => 'rw' );
has _read_from_daemon => ( is => 'rw' );
has _write_to_app => ( is => 'rw' );
has _select => ( is => 'lazy', );
has _window => ( is => 'rw' );
has _bluetooth_printers => ( is => 'rw', default => sub { [] } );
has _usb_printers => ( is => 'rw', default => sub { [] });
has _cat_printers => ( is => 'rw', default => sub { [] });
has _usb_printers => ( is => 'rw', default => sub { [] } );
has _cat_printers => ( is => 'rw', default => sub { [] } );
has _stop_timeout => ( is => 'rw' );
sub _build__select($self) {
@ -38,67 +38,146 @@ sub _read_bluetooth_printers($self) {
my @fhs = $self->_select->can_read(0);
for my $fh (@fhs) {
$fh->blocking(0);
while (defined(my $line = <$fh>)) {
my @return = map {$self->app->device_hash_to_object($_)} @{JSON::from_json($line)};
$self->_bluetooth_printers(\@return);
Exd::DeviceToBluetooth->cache_printers($self->_bluetooth_printers);
while ( defined( my $line = <$fh> ) ) {
my @return = map { $self->app->device_hash_to_object($_) }
@{ JSON::from_json($line) };
$self->_bluetooth_printers( \@return );
Exd::DeviceToBluetooth->cache_printers(
$self->_bluetooth_printers );
}
}
}
sub _usb_printer_print_to_box($self, $device, $box) {
sub _usb_printer_print_to_box( $self, $device, $box ) {
my $window = $self->_window;
my $button = Gtk4::Button->new_with_label($device->output_file);
$button->signal_connect('clicked', sub {
my $button = Gtk4::Button->new_with_label( $device->output_file );
$button->signal_connect(
'clicked',
sub {
$self->instance->device($device);
$window->close;
});
}
);
$box->append($button);
}
sub _cat_printer_print_to_box($self, $device, $box) {
sub _cat_printer_print_to_box( $self, $device, $box ) {
my $window = $self->_window;
my $button = Gtk4::Button->new_with_label($device->path);
$button->signal_connect('clicked', sub {
my $button = Gtk4::Button->new_with_label( $device->path );
$button->signal_connect(
'clicked',
sub {
$self->instance->device($device);
$window->close;
});
}
);
$box->append($button);
}
sub _bluetoth_printer_print_to_box($self, $device, $box) {
sub _bluetoth_printer_print_to_box( $self, $device, $box ) {
my $window = $self->_window;
my $button = Gtk4::Button->new_with_label($device->name.':'.$device->address);
$button->signal_connect('clicked', sub {
my $button =
Gtk4::Button->new_with_label( $device->name . ':' . $device->address );
$button->signal_connect(
'clicked',
sub {
$self->instance->device($device);
$window->close;
});
}
);
$box->append($button);
}
sub _read_usb_printers($self) {
my @printers = map { Exd::DeviceToRawFile->new(output_file => $_) } glob '/dev/usb/lp*';
$self->_usb_printers(\@printers);
my @printers = map { Exd::DeviceToRawFile->new( output_file => $_ ) }
glob '/dev/usb/lp*';
$self->_usb_printers( \@printers );
}
sub _device_api( $self, $path ) {
my $session = Net::DBus->system( private => 1 );
my $service = $session->get_service('org.bluez');
return $service->get_object( $path, 'org.bluez.Device1' );
}
has _pid_read_special_cat_printer => ( is => 'rw' );
sub _try_to_connect_difficult_cat_printer_devices( $self, $key, $device ) {
if ( defined $device && $device->{Name} eq 'SC05-6F4C' ) {
my $device_api = $self->_device_api($key);
if ( $device_api->Connected ) {
return;
}
$device_api->get_service->get_bus->get_connection->disconnect;
if ( defined $self->_pid_read_special_cat_printer && kill 0,
$self->_pid_read_special_cat_printer )
{
# Ignoring if there is a fork running.
return;
}
$self->_pid_read_special_cat_printer(fork);
if ( !$self->_pid_read_special_cat_printer ) {
my $device_api = $self->_device_api($key);
eval {
$device_api->CancelPairing;
sleep 1;
};
if ($@) {
warn $@;
}
eval {
$device_api->Pair;
sleep 1;
};
if ($@) {
warn $@;
}
eval {
$device_api->Connect;
sleep 1;
};
if ($@) {
warn $@;
}
$device_api->get_service->get_bus->get_connection->disconnect;
exit;
}
}
}
sub _read_cat_printers($self) {
my $session = Net::DBus->system;
my $session = Net::DBus->system( private => 1 );
my $service = $session->get_service('org.bluez');
my $path = '/';
my $object_manager_interface = 'org.freedesktop.DBus.ObjectManager';
my $object = $service->get_object($path, $object_manager_interface);
my $object = $service->get_object( $path, $object_manager_interface );
use Data::Dumper;
my $items = $object->GetManagedObjects;
$object->get_service->get_bus->get_connection->disconnect;
my @paths_tx;
for my $key (keys %$items) {
for my $key ( keys %$items ) {
my $device = $items->{$key}{'org.bluez.Device1'};
my $item = $items->{$key}{'org.bluez.GattCharacteristic1'};
if (!defined $item) {
{
next if defined $item;
$self->_try_to_connect_difficult_cat_printer_devices( $key,
$device );
}
if ( !defined $item ) {
next;
}
if ($item->{UUID} eq '0000ae01-0000-1000-8000-00805f9b34fb') {
if ( $item->{UUID} eq '0000ae01-0000-1000-8000-00805f9b34fb' ) {
push @paths_tx, $key;
}
}
$self->_cat_printers([map { Exd::DeviceToCatPrinter->new(path => $_) } sort {$a cmp $b} @paths_tx]);
$self->_cat_printers(
[
map { Exd::DeviceToCatPrinter->new( path => $_ ) }
sort { $a cmp $b } @paths_tx
]
);
}
sub start($self) {
@ -107,15 +186,20 @@ sub start($self) {
my $window = Gtk4::Window->new;
$self->_window($window);
$window->set_default_size( 600, 600 );
$window->signal_connect('close-request', sub{
$window->signal_connect(
'close-request',
sub {
$self->_on_close;
return 0;
});
}
);
$window->set_title('Printer Selection');
$window->set_child(Gtk4::Label->new('Searching for printers...'));
$self->_select->add($self->_read_from_daemon);
$self->_bluetooth_printers(Exd::DeviceToBluetooth->devices_from_cache);
Glib::Timeout->add(1000, sub {
$window->set_child( Gtk4::Label->new('Searching for printers...') );
$self->_select->add( $self->_read_from_daemon );
$self->_bluetooth_printers( Exd::DeviceToBluetooth->devices_from_cache );
Glib::Timeout->add(
1000,
sub {
return 0 if $self->_stop_timeout;
eval {
$self->_read_usb_printers;
@ -124,37 +208,38 @@ sub start($self) {
$self->_update_box;
};
if ($@) {
die $@;
warn $@;
}
return 1;
});
}
);
$window->present;
$window->set_transient_for($self->instance->window);
$window->set_transient_for( $self->instance->window );
}
sub _update_box($self) {
my $window = $self->_window;
my $box = Gtk4::Box->new( 'vertical', 10 );
my $bluetooth_printers = $self->_bluetooth_printers;
for my $device ($bluetooth_printers->@*) {
$self->_bluetoth_printer_print_to_box($device, $box);
for my $device ( $bluetooth_printers->@* ) {
$self->_bluetoth_printer_print_to_box( $device, $box );
}
for my $device ($self->_usb_printers->@*) {
$self->_usb_printer_print_to_box($device, $box);
for my $device ( $self->_usb_printers->@* ) {
$self->_usb_printer_print_to_box( $device, $box );
}
for my $device ($self->_cat_printers->@*) {
$self->_cat_printer_print_to_box($device, $box);
for my $device ( $self->_cat_printers->@* ) {
$self->_cat_printer_print_to_box( $device, $box );
}
$window->set_child($box);
}
sub _start_daemon_bluetooth_search($self) {
my ($read, $write);
my ( $read, $write );
pipe $read, $write;
$self->_read_from_daemon($read);
$self->_write_to_app($write);
my $pid = fork;
if (!$pid) {
if ( !$pid ) {
close $read;
while (1) {
$self->_daemon_bluetooth_search_iteration;
@ -166,25 +251,35 @@ sub _start_daemon_bluetooth_search($self) {
}
sub _daemon_bluetooth_search_iteration($self) {
# say 'Listing bluetooth devices';
if (!kill 0, $self->app->parent_pid) {
# say 'Listing bluetooth devices';
if ( !kill 0, $self->app->parent_pid ) {
say 'Parent died at bluetooth search iteration, exiting...';
exit 1;
}
my $devices = Net::Bluetooth::get_remote_devices;
my @return;
for my $address (keys %$devices) {
say "Detected $devices->{$address}:$address, looking if it supports Serial Port...";
my $search = Net::Bluetooth::sdp_search($address, "1101", "");
if (defined $search && defined $search->{RFCOMM}) {
say "$devices->{$address}:$address supports Serial Port and may be a printer, the port is: $search->{RFCOMM}";
for my $address ( keys %$devices ) {
next if $devices->{$address} eq 'SC05-6F4C';
say
"Detected $devices->{$address}:$address, looking if it supports Serial Port...";
my $search = Net::Bluetooth::sdp_search( $address, "1101", "" );
if ( defined $search && defined $search->{RFCOMM} ) {
say
"$devices->{$address}:$address supports Serial Port and may be a printer, the port is: $search->{RFCOMM}";
say $devices->{$address};
push @return, Exd::DeviceToBluetooth->new(name => $devices->{$address}, address => $address, port => $search->{RFCOMM});
push @return,
Exd::DeviceToBluetooth->new(
name => $devices->{$address},
address => $address,
port => $search->{RFCOMM}
);
}
}
# say 'Finishing listing bluetooth devices';
# say 'Finishing listing bluetooth devices';
@return = map { $_->serialize } sort { $a->name cmp $b->name } @return;
$self->_write_to_app->say(JSON::to_json(\@return));
$self->_write_to_app->say( JSON::to_json( \@return ) );
$self->_write_to_app->flush;
}

View File

@ -121,7 +121,7 @@ sub print($self) {
if system(
qw/convert/,
$image_file_in,
qw/-resize 384x -brightness-contrast +10 -dither FloydSteinberg -remap pattern:gray50/,
qw/-resize 384x -brightness-contrast +0.8 -dither FloydSteinberg -remap pattern:gray50/,
$image_file_out
);
$image_final = Exd::Utils::get_gd_image( $image_file_out . '' );

7
style-dark.css Normal file
View File

@ -0,0 +1,7 @@
headerbar box {
background: #015544;
}
box.paywall {
background: #010F0C;
}

15
style.css Normal file
View File

@ -0,0 +1,15 @@
headerbar box {
background: #48E1C0;
}
button {
min-width: 0;
}
box.paywall {
background: white;
}
box.avoid-main-input {
background: black;
}