Compare commits
551 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
382080ae4e | ||
|
|
66ac529bca | ||
|
|
ab72207027 | ||
|
|
e3aebf1ca2 | ||
|
|
0e5820c038 | ||
|
|
780e4d34df | ||
|
|
a8700572b7 | ||
|
|
055516312a | ||
|
|
76b4a6585e | ||
|
|
b57535c5ad | ||
|
|
eb10a15eee | ||
|
|
30ed362a8c | ||
|
|
34f071ed1d | ||
|
|
3d3d641e1c | ||
|
|
10c11f2c3d | ||
|
|
cec2745dc0 | ||
|
|
8d15edb063 | ||
|
|
38cf3dc141 | ||
|
|
c146290e07 | ||
|
|
a1745289be | ||
|
|
5a7dbfc87a | ||
|
|
f645950a8f | ||
|
|
b273da0b5e | ||
|
|
689f1eb0c5 | ||
|
|
b0a2edf5fa | ||
|
|
e7b5e02657 | ||
|
|
2d4efd6cf0 | ||
|
|
defc0ada78 | ||
|
|
2f3f4f609c | ||
|
|
4a9c9f8cd4 | ||
|
|
bdc089290d | ||
|
|
cf9f48d8da | ||
|
|
2d67279180 | ||
|
|
83e10aac27 | ||
|
|
9972124aa1 | ||
|
|
d0eb1ba96e | ||
|
|
f5d2910638 | ||
|
|
1cafaf3a79 | ||
|
|
fccc133cb2 | ||
|
|
9fb50b8a73 | ||
|
|
c66c1e17d3 | ||
|
|
4496760340 | ||
|
|
506200d2ee | ||
|
|
04898a3b01 | ||
|
|
f67fb53308 | ||
|
|
c5b78dde04 | ||
|
|
57d9c87de6 | ||
|
|
795f95d855 | ||
|
|
c0ebbc8e2f | ||
|
|
fb377c32ea | ||
|
|
e13faff2d7 | ||
|
|
b0c5348116 | ||
|
|
b462ec022a | ||
|
|
e45a0bf24b | ||
|
|
8962644ba8 | ||
|
|
ad5e04bbcc | ||
|
|
6fc7baec39 | ||
|
|
acd49a9c40 | ||
|
|
bf04358a47 | ||
|
|
b7e13c7b86 | ||
|
|
9b45f0661e | ||
|
|
2a61b1202b | ||
|
|
114f862ea4 | ||
|
|
5a490a8b5a | ||
|
|
8fdc1c1b21 | ||
|
|
32cac90720 | ||
|
|
b2160255d3 | ||
|
|
7fe1f4de93 | ||
|
|
1fbb1b1524 | ||
|
|
0105a53765 | ||
|
|
bd5ab80276 | ||
|
|
387d790228 | ||
|
|
d199a2be0d | ||
|
|
f81ecffda6 | ||
|
|
882f80de1e | ||
|
|
4359f2a0ce | ||
|
|
9c485c4d94 | ||
|
|
1330197036 | ||
|
|
0b0d5fa227 | ||
|
|
45424b8a52 | ||
|
|
0956acbdfb | ||
|
|
654f553d8f | ||
|
|
e4b6e20db6 | ||
|
|
b4f6c6f869 | ||
|
|
16edc52bae | ||
|
|
a33e6c03e4 | ||
|
|
4dccb40bf9 | ||
|
|
9ce1981f93 | ||
|
|
6374c77aa8 | ||
|
|
871bb391d6 | ||
|
|
c3903a7b35 | ||
|
|
6aabf82c11 | ||
|
|
89b624aadf | ||
|
|
67317292ee | ||
|
|
42e7f64856 | ||
|
|
79d963fd65 | ||
|
|
44649fd950 | ||
|
|
0e97f99f93 | ||
|
|
4f52ceb3e0 | ||
|
|
61253b5551 | ||
|
|
12bd6f0d9b | ||
|
|
a32010e03b | ||
|
|
4a934c9dab | ||
|
|
20766c1feb | ||
|
|
6cd4de548f | ||
|
|
8ac4dea8f1 | ||
|
|
e8af3e8d3c | ||
|
|
cd05b10168 | ||
|
|
140935bd8c | ||
|
|
ecb122d93c | ||
|
|
c6e08e0039 | ||
|
|
be1e14df81 | ||
|
|
28bca261fb | ||
|
|
2ab5bc1ad2 | ||
|
|
10c57cf6da | ||
|
|
e65c5eeab1 | ||
|
|
85fad35dc2 | ||
|
|
c992768efe | ||
|
|
7b13c0d059 | ||
|
|
2d3f41da6f | ||
|
|
43b9941dc8 | ||
|
|
dcf27f54aa | ||
|
|
fbf6b69e75 | ||
|
|
0bfebd90da | ||
|
|
e7c3dafa36 | ||
|
|
e90a36da79 | ||
|
|
93f33615ad | ||
|
|
99569081c9 | ||
|
|
588a0b3c41 | ||
|
|
4a1118ceb3 | ||
|
|
a65415bc10 | ||
|
|
8a0e66bf11 | ||
|
|
02cda47c28 | ||
|
|
d34a323a81 | ||
|
|
4166ae8db0 | ||
|
|
955b906b52 | ||
|
|
0d424aa81e | ||
|
|
80acbfa56a | ||
|
|
a7cac24004 | ||
|
|
2927bcf09d | ||
|
|
08dee6bb4f | ||
|
|
08c92f906a | ||
|
|
8e9c9802d1 | ||
|
|
138df66d5e | ||
|
|
0a71347e9a | ||
|
|
2a541a7b9c | ||
|
|
705c12e8da | ||
|
|
cf33df1339 | ||
|
|
87e543b5ef | ||
|
|
81caec99b7 | ||
|
|
41484f8673 | ||
|
|
da0d61f36a | ||
|
|
6f3bc74db0 | ||
|
|
af3bd6ec2f | ||
|
|
b5eb13449b | ||
|
|
bd78e8c275 | ||
|
|
2df21081a1 | ||
|
|
ffebff4ea9 | ||
|
|
2885bc99ca | ||
|
|
2657b80adb | ||
|
|
ebfc106701 | ||
|
|
4d60ca951d | ||
|
|
acf5c57599 | ||
|
|
927df5ff39 | ||
|
|
31fc031267 | ||
|
|
02794e0ebd | ||
|
|
ea6cce7068 | ||
|
|
e4cefeaa8f | ||
|
|
d12d5fd8ae | ||
|
|
0617c3fdc0 | ||
|
|
f00eedf57e | ||
|
|
6d36ae6197 | ||
|
|
45125abb8f | ||
|
|
045b0cd075 | ||
|
|
ff3333e1bf | ||
|
|
7663c5e149 | ||
|
|
4d4748a0a8 | ||
|
|
f8f84ed09e | ||
|
|
e7de7ebbfa | ||
|
|
c9f01f4bc4 | ||
|
|
f2675adc05 | ||
|
|
75beaa3684 | ||
|
|
079495cc32 | ||
|
|
c8bd89e56f | ||
|
|
b089ba2e04 | ||
|
|
adbf8495c6 | ||
|
|
ad062862d8 | ||
|
|
3a86a93154 | ||
|
|
dcf0d6f72d | ||
|
|
83d3725240 | ||
|
|
ce1dd69557 | ||
|
|
4081bdd752 | ||
|
|
c3f1e312b3 | ||
|
|
d820878b89 | ||
|
|
7fa1461d5e | ||
|
|
f4b1ef4d04 | ||
|
|
656130a739 | ||
|
|
aa8679dff5 | ||
|
|
f94a3095fd | ||
|
|
e11958dd58 | ||
|
|
a5a251a964 | ||
|
|
8cb1015a35 | ||
|
|
b5dd90b2d5 | ||
|
|
5f7efee00e | ||
|
|
4150e3efde | ||
|
|
8ebcb71e6e | ||
|
|
f3e852c042 | ||
|
|
5e2a07d144 | ||
|
|
25f6231e9d | ||
|
|
beeba88ea5 | ||
|
|
cc3d454d60 | ||
|
|
afb583cff4 | ||
|
|
561fa66393 | ||
|
|
870dc0d36f | ||
|
|
51462dee1e | ||
|
|
c752d28c6a | ||
|
|
d5ca0ca283 | ||
|
|
f371b3a338 | ||
|
|
93fc4a2c86 | ||
|
|
f10d3f86cc | ||
|
|
af17b0015b | ||
|
|
61af1d1c72 | ||
|
|
65780e1672 | ||
|
|
c92a7967ea | ||
|
|
60aed593b3 | ||
|
|
044f347729 | ||
|
|
aec9df1882 | ||
|
|
4f0a2515f8 | ||
|
|
5cde33711e | ||
|
|
64750025f6 | ||
|
|
cd1cbfdffe | ||
|
|
d413f2c3a7 | ||
|
|
ba314dd734 | ||
|
|
9105b7615c | ||
|
|
a9c162476c | ||
|
|
732b37aca0 | ||
|
|
291b5fad7f | ||
|
|
c2a6def8b9 | ||
|
|
9083c578cc | ||
|
|
bcfd1d39bb | ||
|
|
1185b910c4 | ||
|
|
540b6eba04 | ||
|
|
47f4287e64 | ||
|
|
264c6e259b | ||
|
|
0bbe9838c4 | ||
|
|
2fe337eac3 | ||
|
|
5d7caafdf7 | ||
|
|
f416ef925b | ||
|
|
60bd90848b | ||
|
|
b9f4407815 | ||
|
|
f9cd2639ff | ||
|
|
aeb36e8665 | ||
|
|
057482a3e5 | ||
|
|
15721da46e | ||
|
|
f12b82b5ce | ||
|
|
10dc725942 | ||
|
|
5bd5cdf435 | ||
|
|
84b3603c08 | ||
|
|
8f59b96731 | ||
|
|
e1de110dd7 | ||
|
|
8b6fd3d594 | ||
|
|
d7761e8d79 | ||
|
|
751d652a2f | ||
|
|
647e7e708a | ||
|
|
505c0eeae2 | ||
|
|
bf4001968e | ||
|
|
9d222e2c57 | ||
|
|
033300d659 | ||
|
|
4f2b04bd8f | ||
|
|
f8b9bb4b0f | ||
|
|
8ce8e320c3 | ||
|
|
7c0ab4212b | ||
|
|
26633e0982 | ||
|
|
d27a3f1f33 | ||
|
|
89252d0dba | ||
|
|
dbd2edf442 | ||
|
|
dabd6f8284 | ||
|
|
beb3ab463f | ||
|
|
1d67b623e0 | ||
|
|
2c8cde4d91 | ||
|
|
8cc1d48115 | ||
|
|
ec2468fb8d | ||
|
|
7b54cef23b | ||
|
|
a9da8811fc | ||
|
|
60e86b9881 | ||
|
|
a6766f3c99 | ||
|
|
e59c3c6f70 | ||
|
|
49aa344d8b | ||
|
|
4fed6ab298 | ||
|
|
58eab4d3bc | ||
|
|
5ef5da687d | ||
|
|
4875d319dc | ||
|
|
fb5a53435e | ||
|
|
4f805d65b3 | ||
|
|
56ffb0deb1 | ||
|
|
c0c1457073 | ||
|
|
85a0748ad9 | ||
|
|
c1939a36dd | ||
|
|
b7c394b7a5 | ||
|
|
a070681f89 | ||
|
|
d5e424eec8 | ||
|
|
e0366d38f1 | ||
|
|
cbf1d96b16 | ||
|
|
7cc0d6bb5a | ||
|
|
e19b840ee6 | ||
|
|
5c2ca1e3d9 | ||
|
|
059c4beb30 | ||
|
|
427808226f | ||
|
|
5448245942 | ||
|
|
34ab907007 | ||
|
|
e5fde27859 | ||
|
|
699e8e3ddb | ||
|
|
28ee967371 | ||
|
|
1b0b5f2554 | ||
|
|
1bcc86f989 | ||
|
|
f26f932fd7 | ||
|
|
2b7d48ce77 | ||
|
|
111712fd6d | ||
|
|
7609bc181e | ||
|
|
8b05af7ca3 | ||
|
|
20f9108ebf | ||
|
|
cecae7cdc3 | ||
|
|
e5f3123567 | ||
|
|
27b0e27cfd | ||
|
|
890fba0f61 | ||
|
|
c4b732ff93 | ||
|
|
b8fa2985d5 | ||
|
|
ba741fbce8 | ||
|
|
67d070c334 | ||
|
|
8576afe6f5 | ||
|
|
36807fd376 | ||
|
|
2ad1d27a59 | ||
|
|
7cc9c75d15 | ||
|
|
f33609bbf8 | ||
|
|
69eeb4b0f8 | ||
|
|
aa583ec1aa | ||
|
|
2b9f153257 | ||
|
|
cb9b7fc917 | ||
|
|
74b36c8884 | ||
|
|
89ff7d6dae | ||
|
|
91df420cb4 | ||
|
|
fc25c9f0bc | ||
|
|
12a27e3a8d | ||
|
|
125e32ff00 | ||
|
|
4a110633e8 | ||
|
|
65648f8abd | ||
|
|
50174861dc | ||
|
|
486eb1722e | ||
|
|
740ead4059 | ||
|
|
96424be0da | ||
|
|
cd9d659672 | ||
|
|
ca140388d9 | ||
|
|
8fe0229a2c | ||
|
|
1f43de9458 | ||
|
|
2793f38c2d | ||
|
|
42de7de21d | ||
|
|
1072dc0a41 | ||
|
|
9d8f310e30 | ||
|
|
170adfd00c | ||
|
|
d0135a5ff7 | ||
|
|
1c926cca45 | ||
|
|
ace8ecbb4e | ||
|
|
193fa176dc | ||
|
|
054db62cfa | ||
|
|
fe549cf4c5 | ||
|
|
d347e49b6a | ||
|
|
d6a3f7b329 | ||
|
|
4171bc4c70 | ||
|
|
deca5e5021 | ||
|
|
e0923a0494 | ||
|
|
0a1dfeb860 | ||
|
|
1c911575fa | ||
|
|
d2ef0a996f | ||
|
|
1e886cb12c | ||
|
|
984abc89a8 | ||
|
|
3c6c9741ff | ||
|
|
247a11146c | ||
|
|
d1108c533f | ||
|
|
881339848f | ||
|
|
c44638dcbe | ||
|
|
4887b85b8a | ||
|
|
239f58e290 | ||
|
|
560000f69d | ||
|
|
2d3509ae56 | ||
|
|
4cc926a627 | ||
|
|
a499a70633 | ||
|
|
60a9154326 | ||
|
|
f761b8d4e1 | ||
|
|
7a0f6684e5 | ||
|
|
3ed6817ac9 | ||
|
|
ba76385a2f | ||
|
|
7bcd5ba14c | ||
|
|
40db9f7020 | ||
|
|
dffc46551e | ||
|
|
cf92852bb3 | ||
|
|
844c4a28f4 | ||
|
|
d2fc5a6228 | ||
|
|
006da837aa | ||
|
|
d920e27ab3 | ||
|
|
27bafa8ab2 | ||
|
|
907d18a83a | ||
|
|
37b923bea3 | ||
|
|
91e597bbdd | ||
|
|
a0d697bf6f | ||
|
|
63d5018ad6 | ||
|
|
ca928bdacb | ||
|
|
f7e7791b8b | ||
|
|
80c2e5b141 | ||
|
|
63c171e3b4 | ||
|
|
b0bfb0fdd4 | ||
|
|
31b24d9a09 | ||
|
|
8b04a3b16a | ||
|
|
b88d35d7cb | ||
|
|
e00dcf1af0 | ||
|
|
7f23b9b424 | ||
|
|
e9bf04031b | ||
|
|
c8f2334003 | ||
|
|
380b84195f | ||
|
|
fd26137ad2 | ||
|
|
1ad163aac3 | ||
|
|
36dccc8157 | ||
|
|
6dcdf5bf92 | ||
|
|
0b460017b8 | ||
|
|
061b06cd08 | ||
|
|
86272e1788 | ||
|
|
86f3e011d8 | ||
|
|
0367c5bd1c | ||
|
|
eed008de4b | ||
|
|
74f713ee35 | ||
|
|
6dbb8751b2 | ||
|
|
368bb54870 | ||
|
|
77903a5ecd | ||
|
|
9be161d165 | ||
|
|
0ce5b50950 | ||
|
|
9dded5203e | ||
|
|
d1c2188e5c | ||
|
|
c4285a14b2 | ||
|
|
cf0a47e836 | ||
|
|
c38639afcb | ||
|
|
5863593c65 | ||
|
|
c2fa7f9a57 | ||
|
|
41f5cf6bee | ||
|
|
ea508e9c35 | ||
|
|
5ebf7cd273 | ||
|
|
9986088dc4 | ||
|
|
bfe0b2c634 | ||
|
|
cd2af6974c | ||
|
|
c452486573 | ||
|
|
79406b20f2 | ||
|
|
1226bc214d | ||
|
|
7da79dabdf | ||
|
|
b51026a2ee | ||
|
|
67d01f48a3 | ||
|
|
9fd5c5fc1c | ||
|
|
70bc5b83fa | ||
|
|
b380db51fa | ||
|
|
fab598ebff | ||
|
|
6f6d087fa2 | ||
|
|
6e463d1de3 | ||
|
|
21970f3065 | ||
|
|
9d7e44be2d | ||
|
|
9085fb8285 | ||
|
|
dd79d089f6 | ||
|
|
7aaad124d0 | ||
|
|
0025cb9f53 | ||
|
|
15c8f2a3ee | ||
|
|
fc1a2dac90 | ||
|
|
f698b860f7 | ||
|
|
86b057a301 | ||
|
|
b066158a4b | ||
|
|
046c822604 | ||
|
|
d427733bfc | ||
|
|
04d34a06c7 | ||
|
|
69d86513ae | ||
|
|
019b49a219 | ||
|
|
d9e787784a | ||
|
|
71969b88e3 | ||
|
|
7ae0f5e246 | ||
|
|
1ea7da4bb5 | ||
|
|
8b96bb5f27 | ||
|
|
aefce3ccd0 | ||
|
|
4e599e2aba | ||
|
|
4148c289af | ||
|
|
d575ab0b2b | ||
|
|
d09af19d3f | ||
|
|
a2dff17db9 | ||
|
|
c0fecb935f | ||
|
|
28249b7e99 | ||
|
|
eb63e2257f | ||
|
|
2211716d04 | ||
|
|
242137a50c | ||
|
|
c41311bf76 | ||
|
|
e0d959e3c5 | ||
|
|
f0a5c4b2c2 | ||
|
|
e10a50fdc1 | ||
|
|
380f2509ac | ||
|
|
c0fb35f6b9 | ||
|
|
3e658845d2 | ||
|
|
384209ba70 | ||
|
|
835b589894 | ||
|
|
76e684ee75 | ||
|
|
d0d2c83768 | ||
|
|
b476bc3168 | ||
|
|
5a2ad145e8 | ||
|
|
27233d2549 | ||
|
|
1a0bc75629 | ||
|
|
dc04961699 | ||
|
|
e594cf299e | ||
|
|
096c995a91 | ||
|
|
f64b175cd8 | ||
|
|
954e0e8a59 | ||
|
|
034b032cfa | ||
|
|
e33590bff9 | ||
|
|
55f610f3b2 | ||
|
|
87fd93a1cf | ||
|
|
2db77a248a | ||
|
|
f600274592 | ||
|
|
bc37d00a81 | ||
|
|
1956ea95ee | ||
|
|
a56e3b91e1 | ||
|
|
6bcc9d61e1 | ||
|
|
3ef34191a3 | ||
|
|
60de36aff9 | ||
|
|
b019dd1c51 | ||
|
|
3a083d5527 | ||
|
|
8006241a81 | ||
|
|
b05e3d24ee | ||
|
|
b78c0a4979 | ||
|
|
d3b3c309fa | ||
|
|
65615495d9 | ||
|
|
35f448c34f | ||
|
|
e1abd28a88 | ||
|
|
a109d8be64 | ||
|
|
333a0bc05a | ||
|
|
a831519b54 | ||
|
|
a25052ed96 | ||
|
|
676f8dc8be | ||
|
|
9c3cb82fca | ||
|
|
9b827f58ad | ||
|
|
22e327d391 | ||
|
|
451de4d72d | ||
|
|
0679b78c1d | ||
|
|
ad7084b897 | ||
|
|
3c068eaba9 | ||
|
|
819ae08c6a | ||
|
|
38414ad8de | ||
|
|
f0422f7634 | ||
|
|
2a004dadf3 | ||
|
|
b7ea586e44 | ||
|
|
2a5f286d07 | ||
|
|
0bc14671ec |
1
.github/FUNDING.yml
vendored
Normal file
1
.github/FUNDING.yml
vendored
Normal file
@@ -0,0 +1 @@
|
||||
custom: https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=FRJUYV5QP6HW8
|
||||
27
.github/ISSUE_TEMPLATE/bug_report.md
vendored
Normal file
27
.github/ISSUE_TEMPLATE/bug_report.md
vendored
Normal file
@@ -0,0 +1,27 @@
|
||||
---
|
||||
name: Bug report
|
||||
about: Create a report to help us improve
|
||||
title: ''
|
||||
labels: ''
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
**Describe the bug**
|
||||
A clear and concise description of what the bug is.
|
||||
|
||||
**To Reproduce**
|
||||
Steps to reproduce the behavior.
|
||||
|
||||
**Expected behavior**
|
||||
A clear and concise description of what you expected to happen.
|
||||
|
||||
**Screenshots:**
|
||||
If applicable, add screenshots to help explain your problem.
|
||||
|
||||
**System Information:**
|
||||
- Operating system:
|
||||
- Strawberry Version:
|
||||
|
||||
**Additional context**
|
||||
Add any other context about the problem here.
|
||||
20
.github/ISSUE_TEMPLATE/feature_request.md
vendored
Normal file
20
.github/ISSUE_TEMPLATE/feature_request.md
vendored
Normal file
@@ -0,0 +1,20 @@
|
||||
---
|
||||
name: Feature request
|
||||
about: Suggest an idea for this project
|
||||
title: ''
|
||||
labels: ''
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
**Is your feature request related to a problem? Please describe.**
|
||||
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
|
||||
|
||||
**Describe the solution you'd like**
|
||||
A clear and concise description of what you want to happen.
|
||||
|
||||
**Describe alternatives you've considered**
|
||||
A clear and concise description of any alternative solutions or features you've considered.
|
||||
|
||||
**Additional context**
|
||||
Add any other context or screenshots about the feature request here.
|
||||
155
.gitignore
vendored
155
.gitignore
vendored
@@ -1,80 +1,117 @@
|
||||
# This file is used to ignore files which are generated
|
||||
# ----------------------------------------------------------------------------
|
||||
|
||||
*~
|
||||
*.autosave
|
||||
*.a
|
||||
*.core
|
||||
*.moc
|
||||
# Build
|
||||
build/
|
||||
bin/
|
||||
|
||||
# CMake
|
||||
CMakeLists.txt.user
|
||||
CMakeCache.txt
|
||||
CMakeFiles
|
||||
CMakeScripts
|
||||
Makefile*
|
||||
Testing
|
||||
cmake_install.cmake
|
||||
install_manifest.txt
|
||||
compile_commands.json
|
||||
CTestTestfile.cmake
|
||||
_deps
|
||||
|
||||
# Prerequisites
|
||||
*.d
|
||||
|
||||
# Compiled Object files
|
||||
*.slo
|
||||
*.lo
|
||||
*.o
|
||||
*.obj
|
||||
*.orig
|
||||
*.rej
|
||||
|
||||
# Precompiled Headers
|
||||
*.gch
|
||||
*.pch
|
||||
|
||||
# Compiled Dynamic libraries
|
||||
*.so
|
||||
*.so.*
|
||||
*_pch.h.cpp
|
||||
*_resource.rc
|
||||
*.qm
|
||||
.#*
|
||||
*.*#
|
||||
core
|
||||
!core/
|
||||
tags
|
||||
.DS_Store
|
||||
.directory
|
||||
*.debug
|
||||
Makefile*
|
||||
*.prl
|
||||
*.dylib
|
||||
*.dll
|
||||
|
||||
# Fortran module files
|
||||
*.mod
|
||||
*.smod
|
||||
|
||||
# Compiled Static libraries
|
||||
*.lai
|
||||
*.la
|
||||
*.a
|
||||
*.lib
|
||||
|
||||
# Executables
|
||||
*.exe
|
||||
*.out
|
||||
*.app
|
||||
|
||||
# Dump files
|
||||
*.core
|
||||
*.stackdump
|
||||
|
||||
# Qt
|
||||
*build-*
|
||||
moc_*.cpp
|
||||
ui_*.h
|
||||
moc_*.h
|
||||
qrc_*.cpp
|
||||
Thumbs.db
|
||||
*.res
|
||||
*.rc
|
||||
/.qmake.cache
|
||||
/.qmake.stash
|
||||
*.spec
|
||||
*.nsi
|
||||
*.plist
|
||||
maketarball.sh
|
||||
dist/macos/create-dmg.sh
|
||||
dist/debian/changelog
|
||||
dist/pacman/PKGBUILD
|
||||
ui_*.h
|
||||
*.moc
|
||||
*.qm
|
||||
|
||||
# qtcreator generated files
|
||||
*.pro.user*
|
||||
# QtCreator
|
||||
CMakeLists.txt.user*
|
||||
*.pro.user
|
||||
*.pro.user.*
|
||||
*creator.user*
|
||||
target_wrapper.*
|
||||
compile_commands.json
|
||||
|
||||
# xemacs temporary files
|
||||
# Temporary files
|
||||
*~
|
||||
*.autosave
|
||||
*.orig
|
||||
*.rej
|
||||
.*.kate-swp
|
||||
.swp.*
|
||||
.*.swp
|
||||
*.flc
|
||||
|
||||
# Vim temporary files
|
||||
.*.swp
|
||||
|
||||
# Visual Studio generated files
|
||||
*.ib_pdb_index
|
||||
*.idb
|
||||
*.ilk
|
||||
*.pdb
|
||||
*.sln
|
||||
*.suo
|
||||
*.vcproj
|
||||
*vcproj.*.*.user
|
||||
*.ncb
|
||||
*.sdf
|
||||
*.opensdf
|
||||
*.vcxproj
|
||||
*vcxproj.*
|
||||
# Directory files
|
||||
.directory
|
||||
.DS_Store
|
||||
Thumbs.db
|
||||
|
||||
# MinGW generated files
|
||||
*.Debug
|
||||
*.Release
|
||||
|
||||
# Python byte code
|
||||
*.pyc
|
||||
# Package files
|
||||
*.spec
|
||||
*.nsi
|
||||
*.plist
|
||||
|
||||
# Binaries
|
||||
# --------
|
||||
*.dll
|
||||
*.exe
|
||||
# Stuff in dist
|
||||
maketarball.sh
|
||||
create-dmg.sh
|
||||
changelog
|
||||
PKGBUILD
|
||||
|
||||
# Translations
|
||||
translations.pot
|
||||
zanata.xml
|
||||
.zanata-cache/
|
||||
|
||||
# Snap
|
||||
parts/
|
||||
prime/
|
||||
stage/
|
||||
*.snap
|
||||
/snap/.snapcraft/
|
||||
/*_source.tar.bz2
|
||||
|
||||
51
.travis.yml
51
.travis.yml
@@ -10,42 +10,49 @@ compiler:
|
||||
- clang
|
||||
|
||||
before_install:
|
||||
- echo $DEPLOY_KEY_ENC | base64 --decode | openssl aes-256-cbc -K $encrypted_83a41ac424a6_key -iv $encrypted_83a41ac424a6_iv -out ~/.ssh/id_rsa -d
|
||||
- chmod 600 ~/.ssh/id_rsa
|
||||
- if ! [ "$DEPLOY_KEY_ENC" == "" ]; then
|
||||
echo $DEPLOY_KEY_ENC | base64 --decode | openssl aes-256-cbc -K $encrypted_83a41ac424a6_key -iv $encrypted_83a41ac424a6_iv -out ~/.ssh/id_rsa -d ;
|
||||
chmod 600 ~/.ssh/id_rsa ;
|
||||
fi
|
||||
- if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then
|
||||
docker build -f Dockerfile -t strawberry-build .;
|
||||
docker run --name build -itd strawberry-build /bin/bash;
|
||||
docker build -f Dockerfile -t strawberry-build . || travis_terminate 1;
|
||||
docker run --name build -itd strawberry-build /bin/bash || travis_terminate 1;
|
||||
docker exec build git clone https://github.com/jonaski/strawberry;
|
||||
fi
|
||||
- if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then
|
||||
git fetch --unshallow;
|
||||
git pull;
|
||||
brew update;
|
||||
brew unlink python;
|
||||
brew install glib pkgconfig protobuf protobuf-c qt;
|
||||
brew install sqlite --with-fts;
|
||||
brew install gstreamer gst-plugins-base;
|
||||
brew install gst-plugins-good --with-flac;
|
||||
brew install gst-plugins-bad gst-plugins-ugly gst-libav;
|
||||
brew install chromaprint;
|
||||
git fetch --unshallow || travis_terminate 1;
|
||||
git pull || travis_terminate 1;
|
||||
brew update || travis_terminate 1;
|
||||
brew unlink python || travis_terminate 1;
|
||||
brew install glib pkgconfig libffi protobuf protobuf-c qt gettext gnutls fftw sqlite chromaprint;
|
||||
brew install gstreamer gst-plugins-base gst-plugins-good gst-plugins-bad gst-plugins-ugly gst-libav;
|
||||
brew install libcdio libmtp libimobiledevice libplist;
|
||||
brew install create-dmg;
|
||||
export Qt5_DIR=/usr/local/opt/qt5/lib/cmake;
|
||||
export Qt5LinguistTools_DIR=/usr/local/opt/qt5/lib/cmake/Qt5LinguistTools;
|
||||
export PATH="/usr/local/opt/gettext/bin:$PATH";
|
||||
export PKG_CONFIG_PATH=/usr/local/opt/libffi/lib/pkgconfig/:$PKG_CONFIG_PATH;
|
||||
ls /usr/local/lib/gstreamer-1.0;
|
||||
fi
|
||||
before_script:
|
||||
- if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then docker exec build cmake -Hstrawberry -Bbuild -DENABLE_STREAM_DEEZER=ON ; fi
|
||||
- if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then mkdir build; cd build; cmake .. -DUSE_BUNDLE=ON -DENABLE_STREAM_DEEZER=ON ; fi
|
||||
- if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then docker exec build cmake -Hstrawberry -Bbuild ; fi
|
||||
- if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then mkdir build; cd build; cmake .. -DUSE_BUNDLE=ON ; fi
|
||||
script:
|
||||
- if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then docker exec build make -C build -j8 ; fi
|
||||
- if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then
|
||||
make -j8;
|
||||
sudo make install;
|
||||
sudo ../dist/macos/macdeploy.py strawberry.app;
|
||||
../dist/macos/create-dmg.sh strawberry.app;
|
||||
make -j8 || travis_terminate 1;
|
||||
make install || travis_terminate 1;
|
||||
sudo make dmg;
|
||||
fi
|
||||
after_success:
|
||||
- if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then ls -lh strawberry.dmg; fi
|
||||
- if [[ "$TRAVIS_OS_NAME" == "osx" ]] && [[ "$TRAVIS_BRANCH" == "master" ]]; then rsync -e "ssh -o StrictHostKeyChecking=no" -va strawberry*.dmg travis@echoes.jkvinge.net:/home/travis/builds/macos; fi
|
||||
- if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then ls -lh strawberry*.dmg; fi
|
||||
- if [[ "$TRAVIS_OS_NAME" == "osx" ]] && [[ "$CC_FOR_BUILD" == "gcc" ]] && [ -f ~/.ssh/id_rsa ]; then
|
||||
if [[ "$TRAVIS_BRANCH" == "master" ]]; then
|
||||
rsync -e "ssh -o StrictHostKeyChecking=no" -va strawberry*.dmg travis@echoes.jkvinge.net:/home/travis/builds/macos;
|
||||
elif [[ "$TRAVIS_BRANCH" == "macos" ]]; then
|
||||
rsync -e "ssh -o StrictHostKeyChecking=no" -va strawberry*.dmg travis@echoes.jkvinge.net:/home/travis/builds/macos-test;
|
||||
fi
|
||||
fi
|
||||
|
||||
branches:
|
||||
except:
|
||||
|
||||
27
3rdparty/README.md
vendored
27
3rdparty/README.md
vendored
@@ -7,28 +7,11 @@ This is a small static library used by Strawberry to prevent it from starting tw
|
||||
If the user tries to start strawberry twice, the main window will maximize instead of starting another instance.
|
||||
If you dynamically link to your systems version, you'll need two versions, one defined as QApplication and
|
||||
one as a QCoreApplication.
|
||||
It is included here because it is normally not packaged by distros, and is also used on macOS and Windows.
|
||||
It is included here because it is not packed by distros and is also used on macOS and Windows.
|
||||
|
||||
URL: https://github.com/itay-grudev/SingleApplication
|
||||
|
||||
|
||||
qocoa
|
||||
-----
|
||||
This is a small static library currently used for the search fields above the collection, playlist and in
|
||||
the cover manager. It is slightly modified from original version, so it should not be used as a dynamic
|
||||
library.
|
||||
The plan in the long run is to replace it with something else.
|
||||
|
||||
URL: https://github.com/mikemcquaid/Qocoa
|
||||
|
||||
|
||||
SPMediaKeyTap
|
||||
-------------
|
||||
|
||||
This is used for macOS only to enable strawberry to grab global shortcuts and can safely be deleted on other
|
||||
platforms.
|
||||
|
||||
|
||||
taglib
|
||||
------
|
||||
|
||||
@@ -36,11 +19,11 @@ TagLib is a library for reading and editing the meta-data of several popular aud
|
||||
by Strawberry to identify audio files. It is important that it is kept up-to-date for Strawberry to function
|
||||
correctly.
|
||||
|
||||
It is kept in 3rdparty because there currently is no offical release of TagLib with the features and bugfixes
|
||||
It is kept in 3rdparty because there currently is no official release of TagLib with the features and bugfixes
|
||||
that are in the official repository. And also because some distros use older, or unpatched versions.
|
||||
This version is a unmodified copy of commit 5cb589a (sha: 5cb589a5b82c13ba8f0542e5e79629da7645cb3c).
|
||||
|
||||
Also, there is a bug in version 1.11.1 corrupting Ogg files, see: https://github.com/taglib/taglib/issues/864
|
||||
There is a bug in the latest version (1.11.1) corrupting Ogg files,
|
||||
see: https://github.com/taglib/taglib/issues/864
|
||||
If you decide to use the systems taglib, make sure it has been patched with the following commit:
|
||||
https://github.com/taglib/taglib/commit/9336c82da3a04552168f208cd7a5fa4646701ea4
|
||||
|
||||
@@ -54,6 +37,6 @@ URL: https://github.com/taglib/taglib
|
||||
utf8-cpp
|
||||
--------
|
||||
|
||||
This is 2 header files used by taglib, but kept in a seperate directory because it is maintained by others.
|
||||
This is 2 header files used by taglib, but kept in a separate directory because it is maintained by others.
|
||||
|
||||
URL: http://utfcpp.sourceforge.net/
|
||||
|
||||
13
3rdparty/SPMediaKeyTap/CMakeLists.txt
vendored
13
3rdparty/SPMediaKeyTap/CMakeLists.txt
vendored
@@ -1,13 +0,0 @@
|
||||
set(SPMEDIAKEY-SOURCES
|
||||
SPMediaKeyTap.m
|
||||
SPInvocationGrabbing/NSObject+SPInvocationGrabbing.m
|
||||
)
|
||||
|
||||
set(SPMEDIAKEY-HEADERS
|
||||
SPMediaKeyTap.h
|
||||
SPInvocationGrabbing/NSObject+SPInvocationGrabbing.h
|
||||
)
|
||||
|
||||
ADD_LIBRARY(SPMediaKeyTap STATIC
|
||||
${SPMEDIAKEY-SOURCES}
|
||||
)
|
||||
8
3rdparty/SPMediaKeyTap/LICENSE
vendored
8
3rdparty/SPMediaKeyTap/LICENSE
vendored
@@ -1,8 +0,0 @@
|
||||
Copyright (c) 2011, Joachim Bengtsson
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
|
||||
|
||||
* Neither the name of the organization nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
12
3rdparty/SPMediaKeyTap/README.md
vendored
12
3rdparty/SPMediaKeyTap/README.md
vendored
@@ -1,12 +0,0 @@
|
||||
SPMediaKeyTap
|
||||
=============
|
||||
|
||||
`SPMediaKeyTap` abstracts a `CGEventHook` and other nastiness in order to give you a relatively simple API to receive media key events (prev/next/playpause, on F7 to F9 on modern MacBook Pros) exclusively, without them reaching other applications like iTunes. `SPMediaKeyTap` is clever enough to resign its exclusive lock on media keys by looking for which application was active most recently: if that application is in `SPMediaKeyTap`'s whitelist, it will resign the keys. This is similar to the behavior of Apple's applications collaborating on media key handling exclusivity, but unfortunately, Apple are not exposing any APIs allowing third-parties to join in on this collaboration.
|
||||
|
||||
For now, the whitelist is just a hardcoded array in `+[SPMediaKeyTap defaultMediaKeyUserBundleIdentifiers]`. If your app starts using `SPMediaKeyTap`, please [mail me](mailto:nevyn@spotify.com) your bundle ID, and I'll include it in the canonical repository. This is a bad solution; a better solution would be to use distributed notifications to collaborate in creating this whitelist at runtime. Hopefully someone'll have the time and energy to write this soon.
|
||||
|
||||
In `Example/SPMediaKeyTapExampleAppDelegate.m` is an example of both how you use `SPMediaKeyTap`, and how you handle the semi-private `NSEvent` subtypes involved in media keys, including on how to fall back to non-event tap handling of these events.
|
||||
|
||||
`SPMediaKeyTap` and other `CGEventHook`s on the event type `NSSystemDefined` is known to interfere with each other and applications doing weird stuff with mouse input, because mouse clicks are also part of the `NSSystemDefined` category. The single issue we have had reported here at Spotify is Adobe Fireworks, in which item selection stops working with `SPMediaKeyTap` is active.
|
||||
|
||||
`SPMediaKeyTap` requires 10.5 to work, and disables itself on 10.4.
|
||||
@@ -1,30 +0,0 @@
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
@interface SPInvocationGrabber : NSObject {
|
||||
id _object;
|
||||
NSInvocation *_invocation;
|
||||
int frameCount;
|
||||
char **frameStrings;
|
||||
BOOL backgroundAfterForward;
|
||||
BOOL onMainAfterForward;
|
||||
BOOL waitUntilDone;
|
||||
}
|
||||
-(id)initWithObject:(id)obj;
|
||||
-(id)initWithObject:(id)obj stacktraceSaving:(BOOL)saveStack;
|
||||
@property (readonly, retain, nonatomic) id object;
|
||||
@property (readonly, retain, nonatomic) NSInvocation *invocation;
|
||||
@property BOOL backgroundAfterForward;
|
||||
@property BOOL onMainAfterForward;
|
||||
@property BOOL waitUntilDone;
|
||||
-(void)invoke; // will release object and invocation
|
||||
-(void)printBacktrace;
|
||||
-(void)saveBacktrace;
|
||||
@end
|
||||
|
||||
@interface NSObject (SPInvocationGrabbing)
|
||||
-(id)grab;
|
||||
-(id)invokeAfter:(NSTimeInterval)delta;
|
||||
-(id)nextRunloop;
|
||||
-(id)inBackground;
|
||||
-(id)onMainAsync:(BOOL)async;
|
||||
@end
|
||||
@@ -1,128 +0,0 @@
|
||||
#import "NSObject+SPInvocationGrabbing.h"
|
||||
#import <execinfo.h>
|
||||
|
||||
#pragma mark Invocation grabbing
|
||||
@interface SPInvocationGrabber ()
|
||||
@property (readwrite, retain, nonatomic) id object;
|
||||
@property (readwrite, retain, nonatomic) NSInvocation *invocation;
|
||||
|
||||
@end
|
||||
|
||||
@implementation SPInvocationGrabber
|
||||
- (id)initWithObject:(id)obj;
|
||||
{
|
||||
return [self initWithObject:obj stacktraceSaving:YES];
|
||||
}
|
||||
|
||||
-(id)initWithObject:(id)obj stacktraceSaving:(BOOL)saveStack;
|
||||
{
|
||||
self.object = obj;
|
||||
|
||||
if(saveStack)
|
||||
[self saveBacktrace];
|
||||
|
||||
return self;
|
||||
}
|
||||
-(void)dealloc;
|
||||
{
|
||||
free(frameStrings);
|
||||
self.object = nil;
|
||||
self.invocation = nil;
|
||||
[super dealloc];
|
||||
}
|
||||
@synthesize invocation = _invocation, object = _object;
|
||||
|
||||
@synthesize backgroundAfterForward, onMainAfterForward, waitUntilDone;
|
||||
- (void)runInBackground;
|
||||
{
|
||||
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
|
||||
@try {
|
||||
[self invoke];
|
||||
}
|
||||
@finally {
|
||||
[pool drain];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
- (void)forwardInvocation:(NSInvocation *)anInvocation {
|
||||
[anInvocation retainArguments];
|
||||
anInvocation.target = _object;
|
||||
self.invocation = anInvocation;
|
||||
|
||||
if(backgroundAfterForward)
|
||||
[NSThread detachNewThreadSelector:@selector(runInBackground) toTarget:self withObject:nil];
|
||||
else if(onMainAfterForward)
|
||||
[self performSelectorOnMainThread:@selector(invoke) withObject:nil waitUntilDone:waitUntilDone];
|
||||
}
|
||||
- (NSMethodSignature *)methodSignatureForSelector:(SEL)inSelector {
|
||||
NSMethodSignature *signature = [super methodSignatureForSelector:inSelector];
|
||||
if (signature == NULL)
|
||||
signature = [_object methodSignatureForSelector:inSelector];
|
||||
|
||||
return signature;
|
||||
}
|
||||
|
||||
- (void)invoke;
|
||||
{
|
||||
|
||||
@try {
|
||||
[_invocation invoke];
|
||||
}
|
||||
@catch (NSException * e) {
|
||||
NSLog(@"SPInvocationGrabber's target raised %@:\n\t%@\nInvocation was originally scheduled at:", e.name, e);
|
||||
[self printBacktrace];
|
||||
printf("\n");
|
||||
[e raise];
|
||||
}
|
||||
|
||||
self.invocation = nil;
|
||||
self.object = nil;
|
||||
}
|
||||
|
||||
-(void)saveBacktrace;
|
||||
{
|
||||
void *backtraceFrames[128];
|
||||
frameCount = backtrace(&backtraceFrames[0], 128);
|
||||
frameStrings = backtrace_symbols(&backtraceFrames[0], frameCount);
|
||||
}
|
||||
-(void)printBacktrace;
|
||||
{
|
||||
int x;
|
||||
for(x = 3; x < frameCount; x++) {
|
||||
if(frameStrings[x] == NULL) { break; }
|
||||
printf("%s\n", frameStrings[x]);
|
||||
}
|
||||
}
|
||||
@end
|
||||
|
||||
@implementation NSObject (SPInvocationGrabbing)
|
||||
-(id)grab;
|
||||
{
|
||||
return [[[SPInvocationGrabber alloc] initWithObject:self] autorelease];
|
||||
}
|
||||
-(id)invokeAfter:(NSTimeInterval)delta;
|
||||
{
|
||||
id grabber = [self grab];
|
||||
[NSTimer scheduledTimerWithTimeInterval:delta target:grabber selector:@selector(invoke) userInfo:nil repeats:NO];
|
||||
return grabber;
|
||||
}
|
||||
- (id)nextRunloop;
|
||||
{
|
||||
return [self invokeAfter:0];
|
||||
}
|
||||
-(id)inBackground;
|
||||
{
|
||||
SPInvocationGrabber *grabber = [self grab];
|
||||
grabber.backgroundAfterForward = YES;
|
||||
return grabber;
|
||||
}
|
||||
-(id)onMainAsync:(BOOL)async;
|
||||
{
|
||||
SPInvocationGrabber *grabber = [self grab];
|
||||
grabber.onMainAfterForward = YES;
|
||||
grabber.waitUntilDone = !async;
|
||||
return grabber;
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -1,28 +0,0 @@
|
||||
// A
|
||||
+(UIView*)flashAt:(CGRect)r in:(UIView*)parent color:(UIColor*)color;
|
||||
{
|
||||
float duration = 0.5;
|
||||
UIView *flash = [[[UIView alloc] initWithFrame:r] autorelease];
|
||||
flash.backgroundColor = color;
|
||||
[parent addSubview:flash];
|
||||
[[flash invokeAfter:duration+0.1] removeFromSuperview];
|
||||
|
||||
[UIView beginAnimations:@"SPFlash" context:NULL];
|
||||
[UIView setAnimationDuration:duration];
|
||||
flash.alpha = 0.0;
|
||||
[UIView commitAnimations];
|
||||
return flash;
|
||||
}
|
||||
|
||||
// B
|
||||
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
|
||||
|
||||
// Force the animation to happen by calling this method again after a small
|
||||
// delay - see http://blog.instapaper.com/post/53568356
|
||||
[[self nextRunloop] delayedTableViewDidSelectRowAtIndexPath: indexPath];
|
||||
}
|
||||
|
||||
// C
|
||||
[[tableView invokeAfter:0.15] selectRowAtIndexPath:indexPath animated:YES scrollPosition:UITableViewScrollPositionNone];
|
||||
[[tableView invokeAfter:0.30] deselectRowAtIndexPath:indexPath animated:YES];
|
||||
[[tableView invokeAfter:0.45] selectRowAtIndexPath:indexPath animated:YES scrollPosition:UITableViewScrollPositionNone];
|
||||
@@ -1,12 +0,0 @@
|
||||
@interface MyClass : NSObject
|
||||
-(BOOL)areTheNewViewersGoneYet:(Duck*)duck;
|
||||
@end
|
||||
...
|
||||
MyClass *myInstance = [[MyClass alloc] init];
|
||||
id invocationGrabber = [[[SPInvocationGrabber alloc] initWithTarget:myInstance] autorelease];
|
||||
|
||||
|
||||
[invocationGrabber areTheNewViewersGoneYet:[Duck yellowDuck]]; // line 9
|
||||
|
||||
|
||||
NSInvocation *invocationForAreTheNewViewersGoneYet = [invocationGrabber invocation];
|
||||
@@ -1,38 +0,0 @@
|
||||
#import <Cocoa/Cocoa.h>
|
||||
#import "NSObject+SPInvocationGrabbing.h"
|
||||
|
||||
@interface Foo : NSObject {
|
||||
int a;
|
||||
}
|
||||
-(void)startIt;
|
||||
-(void)theBackgroundStuff;
|
||||
-(void)theForegroundStuff;
|
||||
@end
|
||||
|
||||
@implementation Foo
|
||||
-(void)startIt;
|
||||
{
|
||||
NSLog(@"Starting out on the main thread...");
|
||||
a = 3;
|
||||
[[self inBackground] theBackgroundStuff];
|
||||
}
|
||||
-(void)theBackgroundStuff;
|
||||
{
|
||||
NSLog(@"Woah, this is a background thread!");
|
||||
a += 6;
|
||||
[[self onMainAsync:YES] theForegroundStuff];
|
||||
}
|
||||
-(void)theForegroundStuff;
|
||||
{
|
||||
NSLog(@"Hey presto: %d", a);
|
||||
}
|
||||
@end
|
||||
|
||||
int main() {
|
||||
NSAutoreleasePool *pool = [NSAutoreleasePool new];
|
||||
Foo *foo = [Foo new];
|
||||
[foo startIt];
|
||||
[[NSRunLoop currentRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:1]];
|
||||
[pool release];
|
||||
return 0;
|
||||
}
|
||||
34
3rdparty/SPMediaKeyTap/SPMediaKeyTap.h
vendored
34
3rdparty/SPMediaKeyTap/SPMediaKeyTap.h
vendored
@@ -1,34 +0,0 @@
|
||||
#include <Cocoa/Cocoa.h>
|
||||
#import <IOKit/hidsystem/ev_keymap.h>
|
||||
#import <Carbon/Carbon.h>
|
||||
|
||||
// http://overooped.com/post/2593597587/mediakeys
|
||||
|
||||
#define SPSystemDefinedEventMediaKeys 8
|
||||
|
||||
@interface SPMediaKeyTap : NSObject {
|
||||
EventHandlerRef _app_switching_ref;
|
||||
EventHandlerRef _app_terminating_ref;
|
||||
CFMachPortRef _eventPort;
|
||||
CFRunLoopSourceRef _eventPortSource;
|
||||
CFRunLoopRef _tapThreadRL;
|
||||
BOOL _shouldInterceptMediaKeyEvents;
|
||||
id _delegate;
|
||||
// The app that is frontmost in this list owns media keys
|
||||
NSMutableArray *_mediaKeyAppList;
|
||||
}
|
||||
+ (NSArray*)defaultMediaKeyUserBundleIdentifiers;
|
||||
|
||||
-(id)initWithDelegate:(id)delegate;
|
||||
|
||||
+(BOOL)usesGlobalMediaKeyTap;
|
||||
-(void)startWatchingMediaKeys;
|
||||
-(void)stopWatchingMediaKeys;
|
||||
-(void)handleAndReleaseMediaKeyEvent:(NSEvent *)event;
|
||||
@end
|
||||
|
||||
@interface NSObject (SPMediaKeyTapDelegate)
|
||||
-(void)mediaKeyTap:(SPMediaKeyTap*)keyTap receivedMediaKeyEvent:(NSEvent*)event;
|
||||
@end
|
||||
|
||||
extern NSString *kMediaKeyUsingBundleIdentifiersDefaultsKey;
|
||||
300
3rdparty/SPMediaKeyTap/SPMediaKeyTap.m
vendored
300
3rdparty/SPMediaKeyTap/SPMediaKeyTap.m
vendored
@@ -1,300 +0,0 @@
|
||||
// Copyright (c) 2010 Spotify AB
|
||||
#import "SPMediaKeyTap.h"
|
||||
#import "SPInvocationGrabbing/NSObject+SPInvocationGrabbing.h" // https://gist.github.com/511181, in submodule
|
||||
|
||||
@interface SPMediaKeyTap ()
|
||||
-(BOOL)shouldInterceptMediaKeyEvents;
|
||||
-(void)setShouldInterceptMediaKeyEvents:(BOOL)newSetting;
|
||||
-(void)startWatchingAppSwitching;
|
||||
-(void)stopWatchingAppSwitching;
|
||||
-(void)eventTapThread;
|
||||
@end
|
||||
static SPMediaKeyTap *singleton = nil;
|
||||
|
||||
static pascal OSStatus appSwitched (EventHandlerCallRef nextHandler, EventRef evt, void* userData);
|
||||
static pascal OSStatus appTerminated (EventHandlerCallRef nextHandler, EventRef evt, void* userData);
|
||||
static CGEventRef tapEventCallback(CGEventTapProxy proxy, CGEventType type, CGEventRef event, void *refcon);
|
||||
|
||||
|
||||
// Inspired by http://gist.github.com/546311
|
||||
|
||||
@implementation SPMediaKeyTap
|
||||
|
||||
#pragma mark -
|
||||
#pragma mark Setup and teardown
|
||||
-(id)initWithDelegate:(id)delegate;
|
||||
{
|
||||
_delegate = delegate;
|
||||
[self startWatchingAppSwitching];
|
||||
singleton = self;
|
||||
_mediaKeyAppList = [NSMutableArray new];
|
||||
return self;
|
||||
}
|
||||
-(void)dealloc;
|
||||
{
|
||||
[self stopWatchingMediaKeys];
|
||||
[self stopWatchingAppSwitching];
|
||||
[_mediaKeyAppList release];
|
||||
[super dealloc];
|
||||
}
|
||||
|
||||
-(void)startWatchingAppSwitching;
|
||||
{
|
||||
// Listen to "app switched" event, so that we don't intercept media keys if we
|
||||
// weren't the last "media key listening" app to be active
|
||||
EventTypeSpec eventType = { kEventClassApplication, kEventAppFrontSwitched };
|
||||
OSStatus err = InstallApplicationEventHandler(NewEventHandlerUPP(appSwitched), 1, &eventType, self, &_app_switching_ref);
|
||||
assert(err == noErr);
|
||||
|
||||
eventType.eventKind = kEventAppTerminated;
|
||||
err = InstallApplicationEventHandler(NewEventHandlerUPP(appTerminated), 1, &eventType, self, &_app_terminating_ref);
|
||||
assert(err == noErr);
|
||||
}
|
||||
-(void)stopWatchingAppSwitching;
|
||||
{
|
||||
if(!_app_switching_ref) return;
|
||||
RemoveEventHandler(_app_switching_ref);
|
||||
_app_switching_ref = NULL;
|
||||
}
|
||||
|
||||
-(void)startWatchingMediaKeys;{
|
||||
[self setShouldInterceptMediaKeyEvents:YES];
|
||||
|
||||
// Add an event tap to intercept the system defined media key events
|
||||
_eventPort = CGEventTapCreate(kCGSessionEventTap,
|
||||
kCGHeadInsertEventTap,
|
||||
kCGEventTapOptionDefault,
|
||||
CGEventMaskBit(NX_SYSDEFINED),
|
||||
tapEventCallback,
|
||||
self);
|
||||
assert(_eventPort != NULL);
|
||||
|
||||
_eventPortSource = CFMachPortCreateRunLoopSource(kCFAllocatorSystemDefault, _eventPort, 0);
|
||||
assert(_eventPortSource != NULL);
|
||||
|
||||
// Let's do this in a separate thread so that a slow app doesn't lag the event tap
|
||||
[NSThread detachNewThreadSelector:@selector(eventTapThread) toTarget:self withObject:nil];
|
||||
}
|
||||
-(void)stopWatchingMediaKeys;
|
||||
{
|
||||
// TODO<nevyn>: Shut down thread, remove event tap port and source
|
||||
}
|
||||
|
||||
#pragma mark -
|
||||
#pragma mark Accessors
|
||||
|
||||
+(BOOL)usesGlobalMediaKeyTap
|
||||
{
|
||||
#ifdef _DEBUG
|
||||
// breaking in gdb with a key tap inserted sometimes locks up all mouse and keyboard input forever, forcing reboot
|
||||
return NO;
|
||||
#else
|
||||
// XXX(nevyn): MediaKey event tap doesn't work on 10.4, feel free to figure out why if you have the energy.
|
||||
return floor(NSAppKitVersionNumber) >= 949/*NSAppKitVersionNumber10_5*/;
|
||||
#endif
|
||||
}
|
||||
|
||||
+ (NSArray*)defaultMediaKeyUserBundleIdentifiers;
|
||||
{
|
||||
return [NSArray arrayWithObjects:
|
||||
[[NSBundle mainBundle] bundleIdentifier], // your app
|
||||
@"com.spotify.client",
|
||||
@"com.apple.iTunes",
|
||||
@"com.apple.QuickTimePlayerX",
|
||||
@"com.apple.quicktimeplayer",
|
||||
@"com.apple.iWork.Keynote",
|
||||
@"com.apple.iPhoto",
|
||||
@"org.videolan.vlc",
|
||||
@"com.apple.Aperture",
|
||||
@"com.plexsquared.Plex",
|
||||
@"com.soundcloud.desktop",
|
||||
@"com.macromedia.fireworks", // the tap messes up their mouse input
|
||||
nil
|
||||
];
|
||||
}
|
||||
|
||||
|
||||
-(BOOL)shouldInterceptMediaKeyEvents;
|
||||
{
|
||||
BOOL shouldIntercept = NO;
|
||||
@synchronized(self) {
|
||||
shouldIntercept = _shouldInterceptMediaKeyEvents;
|
||||
}
|
||||
return shouldIntercept;
|
||||
}
|
||||
|
||||
-(void)pauseTapOnTapThread:(BOOL)yeahno;
|
||||
{
|
||||
CGEventTapEnable(self->_eventPort, yeahno);
|
||||
}
|
||||
-(void)setShouldInterceptMediaKeyEvents:(BOOL)newSetting;
|
||||
{
|
||||
BOOL oldSetting;
|
||||
@synchronized(self) {
|
||||
oldSetting = _shouldInterceptMediaKeyEvents;
|
||||
_shouldInterceptMediaKeyEvents = newSetting;
|
||||
}
|
||||
if(_tapThreadRL && oldSetting != newSetting) {
|
||||
id grab = [self grab];
|
||||
[grab pauseTapOnTapThread:newSetting];
|
||||
NSTimer *timer = [NSTimer timerWithTimeInterval:0 invocation:[grab invocation] repeats:NO];
|
||||
CFRunLoopAddTimer(_tapThreadRL, (CFRunLoopTimerRef)timer, kCFRunLoopCommonModes);
|
||||
}
|
||||
}
|
||||
|
||||
#pragma mark
|
||||
#pragma mark -
|
||||
#pragma mark Event tap callbacks
|
||||
|
||||
// Note: method called on background thread
|
||||
|
||||
static CGEventRef tapEventCallback2(CGEventTapProxy proxy, CGEventType type, CGEventRef event, void *refcon)
|
||||
{
|
||||
SPMediaKeyTap *self = refcon;
|
||||
|
||||
if(type == kCGEventTapDisabledByTimeout) {
|
||||
NSLog(@"Media key event tap was disabled by timeout");
|
||||
CGEventTapEnable(self->_eventPort, TRUE);
|
||||
return event;
|
||||
} else if(type == kCGEventTapDisabledByUserInput) {
|
||||
// Was disabled manually by -[pauseTapOnTapThread]
|
||||
return event;
|
||||
}
|
||||
NSEvent *nsEvent = nil;
|
||||
@try {
|
||||
nsEvent = [NSEvent eventWithCGEvent:event];
|
||||
}
|
||||
@catch (NSException * e) {
|
||||
NSLog(@"Strange CGEventType: %d: %@", type, e);
|
||||
assert(0);
|
||||
return event;
|
||||
}
|
||||
|
||||
if (type != NX_SYSDEFINED || [nsEvent subtype] != SPSystemDefinedEventMediaKeys)
|
||||
return event;
|
||||
|
||||
int keyCode = (([nsEvent data1] & 0xFFFF0000) >> 16);
|
||||
if (keyCode != NX_KEYTYPE_PLAY && keyCode != NX_KEYTYPE_FAST && keyCode != NX_KEYTYPE_REWIND)
|
||||
return event;
|
||||
|
||||
if (![self shouldInterceptMediaKeyEvents])
|
||||
return event;
|
||||
|
||||
[nsEvent retain]; // matched in handleAndReleaseMediaKeyEvent:
|
||||
[self performSelectorOnMainThread:@selector(handleAndReleaseMediaKeyEvent:) withObject:nsEvent waitUntilDone:NO];
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static CGEventRef tapEventCallback(CGEventTapProxy proxy, CGEventType type, CGEventRef event, void *refcon)
|
||||
{
|
||||
NSAutoreleasePool *pool = [NSAutoreleasePool new];
|
||||
CGEventRef ret = tapEventCallback2(proxy, type, event, refcon);
|
||||
[pool drain];
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
// event will have been retained in the other thread
|
||||
-(void)handleAndReleaseMediaKeyEvent:(NSEvent *)event {
|
||||
[event autorelease];
|
||||
|
||||
[_delegate mediaKeyTap:self receivedMediaKeyEvent:event];
|
||||
}
|
||||
|
||||
|
||||
-(void)eventTapThread;
|
||||
{
|
||||
_tapThreadRL = CFRunLoopGetCurrent();
|
||||
CFRunLoopAddSource(_tapThreadRL, _eventPortSource, kCFRunLoopCommonModes);
|
||||
CFRunLoopRun();
|
||||
}
|
||||
|
||||
#pragma mark Task switching callbacks
|
||||
|
||||
NSString *kMediaKeyUsingBundleIdentifiersDefaultsKey = @"SPApplicationsNeedingMediaKeys";
|
||||
|
||||
|
||||
-(void)mediaKeyAppListChanged;
|
||||
{
|
||||
if([_mediaKeyAppList count] == 0) return;
|
||||
|
||||
/*NSLog(@"--");
|
||||
int i = 0;
|
||||
for (NSValue *psnv in _mediaKeyAppList) {
|
||||
ProcessSerialNumber psn; [psnv getValue:&psn];
|
||||
NSDictionary *processInfo = [(id)ProcessInformationCopyDictionary(
|
||||
&psn,
|
||||
kProcessDictionaryIncludeAllInformationMask
|
||||
) autorelease];
|
||||
NSString *bundleIdentifier = [processInfo objectForKey:(id)kCFBundleIdentifierKey];
|
||||
NSLog(@"%d: %@", i++, bundleIdentifier);
|
||||
}*/
|
||||
|
||||
ProcessSerialNumber mySerial, topSerial;
|
||||
GetCurrentProcess(&mySerial);
|
||||
[[_mediaKeyAppList objectAtIndex:0] getValue:&topSerial];
|
||||
|
||||
Boolean same;
|
||||
OSErr err = SameProcess(&mySerial, &topSerial, &same);
|
||||
[self setShouldInterceptMediaKeyEvents:(err == noErr && same)];
|
||||
|
||||
}
|
||||
-(void)appIsNowFrontmost:(ProcessSerialNumber)psn;
|
||||
{
|
||||
NSValue *psnv = [NSValue valueWithBytes:&psn objCType:@encode(ProcessSerialNumber)];
|
||||
|
||||
NSDictionary *processInfo = [(id)ProcessInformationCopyDictionary(
|
||||
&psn,
|
||||
kProcessDictionaryIncludeAllInformationMask
|
||||
) autorelease];
|
||||
NSString *bundleIdentifier = [processInfo objectForKey:(id)kCFBundleIdentifierKey];
|
||||
|
||||
NSArray *whitelistIdentifiers = [[NSUserDefaults standardUserDefaults] arrayForKey:kMediaKeyUsingBundleIdentifiersDefaultsKey];
|
||||
if(![whitelistIdentifiers containsObject:bundleIdentifier]) return;
|
||||
|
||||
[_mediaKeyAppList removeObject:psnv];
|
||||
[_mediaKeyAppList insertObject:psnv atIndex:0];
|
||||
[self mediaKeyAppListChanged];
|
||||
}
|
||||
-(void)appTerminated:(ProcessSerialNumber)psn;
|
||||
{
|
||||
NSValue *psnv = [NSValue valueWithBytes:&psn objCType:@encode(ProcessSerialNumber)];
|
||||
[_mediaKeyAppList removeObject:psnv];
|
||||
[self mediaKeyAppListChanged];
|
||||
}
|
||||
|
||||
static pascal OSStatus appSwitched (EventHandlerCallRef nextHandler, EventRef evt, void* userData)
|
||||
{
|
||||
SPMediaKeyTap *self = (id)userData;
|
||||
|
||||
ProcessSerialNumber newSerial;
|
||||
GetFrontProcess(&newSerial);
|
||||
|
||||
[self appIsNowFrontmost:newSerial];
|
||||
|
||||
return CallNextEventHandler(nextHandler, evt);
|
||||
}
|
||||
|
||||
static pascal OSStatus appTerminated (EventHandlerCallRef nextHandler, EventRef evt, void* userData)
|
||||
{
|
||||
SPMediaKeyTap *self = (id)userData;
|
||||
|
||||
ProcessSerialNumber deadPSN;
|
||||
|
||||
GetEventParameter(
|
||||
evt,
|
||||
kEventParamProcessID,
|
||||
typeProcessSerialNumber,
|
||||
NULL,
|
||||
sizeof(deadPSN),
|
||||
NULL,
|
||||
&deadPSN
|
||||
);
|
||||
|
||||
|
||||
[self appTerminated:deadPSN];
|
||||
return CallNextEventHandler(nextHandler, evt);
|
||||
}
|
||||
|
||||
@end
|
||||
25
3rdparty/SPMediaKeyTap/SPMediaKeyTapDelegate.m
vendored
25
3rdparty/SPMediaKeyTap/SPMediaKeyTapDelegate.m
vendored
@@ -1,25 +0,0 @@
|
||||
-(void)mediaKeyTap:(SPMediaKeyTap*)keyTap receivedMediaKeyEvent:(NSEvent*)event;
|
||||
{
|
||||
assert([event type] == NSSystemDefined && [event subtype] == SPSystemDefinedEventMediaKeys);
|
||||
|
||||
int keyCode = (([event data1] & 0xFFFF0000) >> 16);
|
||||
int keyFlags = ([event data1] & 0x0000FFFF);
|
||||
int keyState = (((keyFlags & 0xFF00) >> 8)) == 0xA;
|
||||
int keyRepeat = (keyFlags & 0x1);
|
||||
|
||||
if (keyState == 1 && windowController != NULL) {
|
||||
|
||||
|
||||
switch (keyCode) {
|
||||
|
||||
case NX_KEYTYPE_PLAY:
|
||||
... return;
|
||||
|
||||
case NX_KEYTYPE_FAST:
|
||||
... return;
|
||||
|
||||
case NX_KEYTYPE_REWIND:
|
||||
... return;
|
||||
}
|
||||
}
|
||||
}
|
||||
35
3rdparty/qocoa/CMakeLists.txt
vendored
35
3rdparty/qocoa/CMakeLists.txt
vendored
@@ -1,35 +0,0 @@
|
||||
cmake_minimum_required(VERSION 2.8.11)
|
||||
set(CMAKE_CXX_STANDARD 11)
|
||||
|
||||
set(SOURCES)
|
||||
|
||||
set(HEADERS
|
||||
qsearchfield.h
|
||||
qbutton.h
|
||||
qprogressindicatorspinning.h
|
||||
)
|
||||
|
||||
qt5_wrap_cpp(MOC_SOURCES ${HEADERS})
|
||||
|
||||
if(APPLE)
|
||||
list(APPEND SOURCES
|
||||
qsearchfield_mac.mm
|
||||
qbutton_mac.mm
|
||||
qprogressindicatorspinning_mac.mm
|
||||
)
|
||||
else()
|
||||
list(APPEND SOURCES
|
||||
qsearchfield_nonmac.cpp
|
||||
qbutton_nonmac.cpp
|
||||
qprogressindicatorspinning_nonmac.cpp
|
||||
)
|
||||
set(RESOURCES
|
||||
qsearchfield_nonmac.qrc
|
||||
qprogressindicatorspinning_nonmac.qrc
|
||||
)
|
||||
qt5_add_resources(RESOURCES_SOURCES ${RESOURCES})
|
||||
endif()
|
||||
|
||||
add_library(Qocoa STATIC ${SOURCES} ${MOC_SOURCES} ${RESOURCES_SOURCES})
|
||||
target_link_libraries(Qocoa ${QT_LIBRARIES})
|
||||
|
||||
19
3rdparty/qocoa/LICENSE.txt
vendored
19
3rdparty/qocoa/LICENSE.txt
vendored
@@ -1,19 +0,0 @@
|
||||
Copyright (C) 2011 by Mike McQuaid
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
38
3rdparty/qocoa/README.md
vendored
38
3rdparty/qocoa/README.md
vendored
@@ -1,38 +0,0 @@
|
||||
# Qocoa
|
||||
Qocoa is a collection of Qt wrappers for OSX's Cocoa widgets.
|
||||
|
||||
## Features
|
||||
- basic fallback to sensible Qt types on non-OSX platforms
|
||||
- shared class headers which expose no implementation details
|
||||
- typical Qt signal/slot-based API
|
||||
- trivial to import into projects (class header/implementation, [single shared global header](https://github.com/mikemcquaid/Qocoa/blob/master/qocoa_mac.h))
|
||||
|
||||
## Building
|
||||
```
|
||||
git clone git://github.com/mikemcquaid/Qocoa.git
|
||||
cd Qocoa
|
||||
qmake # or cmake .
|
||||
make
|
||||
```
|
||||
|
||||
## Status
|
||||
I'm not personally working on this any more but will accept pull-requests.
|
||||
|
||||
[](https://travis-ci.org/MikeMcQuaid/Qocoa)
|
||||
|
||||
## Usage
|
||||
For each class you want to use copy the [`qocoa_mac.h`](https://github.com/mikemcquaid/Qocoa/blob/master/qocoa_mac.h), `$CLASS.h`, `$CLASS_mac.*` and `$CLASS_nonmac.*` files into your source tree and add them to your buildsystem. Examples are provided for [CMake](https://github.com/mikemcquaid/Qocoa/blob/master/CMakeLists.txt) and [QMake](https://github.com/mikemcquaid/Qocoa/blob/master/Qocoa.pro).
|
||||
|
||||
## Contact
|
||||
[Mike McQuaid](mailto:mike@mikemcquaid.com)
|
||||
|
||||
## License
|
||||
Qocoa is licensed under the [MIT License](http://en.wikipedia.org/wiki/MIT_License).
|
||||
The full license text is available in [LICENSE.txt](https://github.com/mikemcquaid/Qocoa/blob/master/LICENSE.txt).
|
||||
|
||||
Magnifier and EditClear icons taken from [QtCreator](http://qt-project.org/) and are licensed under the [LGPL](http://www.gnu.org/copyleft/lesser.html).
|
||||
|
||||
Other icons are taken from the [Oxygen Project](http://www.oxygen-icons.org/) and are licensed under the [Creative Commons Attribution-ShareAlike 3.0 License](http://creativecommons.org/licenses/by-sa/3.0/).
|
||||
|
||||
## Gallery
|
||||

|
||||
13
3rdparty/qocoa/TODO.md
vendored
13
3rdparty/qocoa/TODO.md
vendored
@@ -1,13 +0,0 @@
|
||||
Widgets I hope to implement (or at least investigate):
|
||||
|
||||
- NSTokenField
|
||||
- NSSegmentedControl
|
||||
- NSLevelIndicator
|
||||
- NSPathControl
|
||||
- NSSlider (Circular)
|
||||
- NSSplitView
|
||||
- NSTextFinder
|
||||
- NSOutlineView in an NSScrollView (Source List)
|
||||
- NSDrawer
|
||||
- PDFView
|
||||
- WebView
|
||||
49
3rdparty/qocoa/qbutton.h
vendored
49
3rdparty/qocoa/qbutton.h
vendored
@@ -1,49 +0,0 @@
|
||||
#ifndef QBUTTON_H
|
||||
#define QBUTTON_H
|
||||
|
||||
#include <QWidget>
|
||||
#include <QPointer>
|
||||
|
||||
class QButtonPrivate;
|
||||
class QButton : public QWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
// Matches NSBezelStyle
|
||||
enum BezelStyle {
|
||||
Rounded = 1,
|
||||
RegularSquare = 2,
|
||||
Disclosure = 5,
|
||||
ShadowlessSquare = 6,
|
||||
Circular = 7,
|
||||
TexturedSquare = 8,
|
||||
HelpButton = 9,
|
||||
SmallSquare = 10,
|
||||
TexturedRounded = 11,
|
||||
RoundRect = 12,
|
||||
Recessed = 13,
|
||||
RoundedDisclosure = 14,
|
||||
#ifdef __MAC_10_7
|
||||
Inline = 15
|
||||
#endif
|
||||
};
|
||||
|
||||
explicit QButton(QWidget *parent, BezelStyle bezelStyle = Rounded);
|
||||
|
||||
public slots:
|
||||
void setText(const QString &text);
|
||||
void setImage(const QPixmap &image);
|
||||
void setChecked(bool checked);
|
||||
|
||||
public:
|
||||
void setCheckable(bool checkable);
|
||||
bool isChecked();
|
||||
|
||||
signals:
|
||||
void clicked(bool checked = false);
|
||||
|
||||
private:
|
||||
friend class QButtonPrivate;
|
||||
QPointer<QButtonPrivate> pimpl;
|
||||
};
|
||||
#endif // QBUTTON_H
|
||||
229
3rdparty/qocoa/qbutton_mac.mm
vendored
229
3rdparty/qocoa/qbutton_mac.mm
vendored
@@ -1,229 +0,0 @@
|
||||
/*
|
||||
Copyright (C) 2011 by Mike McQuaid
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "qbutton.h"
|
||||
|
||||
#include "qocoa_mac.h"
|
||||
|
||||
#import "Foundation/NSAutoreleasePool.h"
|
||||
#import "AppKit/NSButton.h"
|
||||
#import "AppKit/NSFont.h"
|
||||
|
||||
class QButtonPrivate : public QObject
|
||||
{
|
||||
public:
|
||||
QButtonPrivate(QButton *qButton, NSButton *nsButton, QButton::BezelStyle bezelStyle)
|
||||
: QObject(qButton), qButton(qButton), nsButton(nsButton)
|
||||
{
|
||||
switch(bezelStyle) {
|
||||
case QButton::Disclosure:
|
||||
case QButton::Circular:
|
||||
#ifdef __MAC_10_7
|
||||
case QButton::Inline:
|
||||
#endif
|
||||
case QButton::RoundedDisclosure:
|
||||
case QButton::HelpButton:
|
||||
[nsButton setTitle:@""];
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
NSFont* font = 0;
|
||||
switch(bezelStyle) {
|
||||
case QButton::RoundRect:
|
||||
font = [NSFont fontWithName:@"Lucida Grande" size:12];
|
||||
break;
|
||||
|
||||
case QButton::Recessed:
|
||||
font = [NSFont fontWithName:@"Lucida Grande Bold" size:12];
|
||||
break;
|
||||
|
||||
#ifdef __MAC_10_7
|
||||
case QButton::Inline:
|
||||
font = [NSFont boldSystemFontOfSize:[NSFont systemFontSizeForControlSize:NSSmallControlSize]];
|
||||
break;
|
||||
#endif
|
||||
|
||||
default:
|
||||
font = [NSFont systemFontOfSize:[NSFont systemFontSizeForControlSize:NSRegularControlSize]];
|
||||
break;
|
||||
}
|
||||
[nsButton setFont:font];
|
||||
|
||||
switch(bezelStyle) {
|
||||
case QButton::Rounded:
|
||||
qButton->setMinimumWidth(40);
|
||||
qButton->setFixedHeight(24);
|
||||
qButton->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Fixed);
|
||||
break;
|
||||
case QButton::RegularSquare:
|
||||
case QButton::TexturedSquare:
|
||||
qButton->setMinimumSize(14, 23);
|
||||
qButton->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum);
|
||||
break;
|
||||
case QButton::ShadowlessSquare:
|
||||
qButton->setMinimumSize(5, 25);
|
||||
qButton->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum);
|
||||
break;
|
||||
case QButton::SmallSquare:
|
||||
qButton->setMinimumSize(4, 21);
|
||||
qButton->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum);
|
||||
break;
|
||||
case QButton::TexturedRounded:
|
||||
qButton->setMinimumSize(10, 22);
|
||||
qButton->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum);
|
||||
break;
|
||||
case QButton::RoundRect:
|
||||
case QButton::Recessed:
|
||||
qButton->setMinimumWidth(16);
|
||||
qButton->setFixedHeight(18);
|
||||
qButton->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Fixed);
|
||||
break;
|
||||
case QButton::Disclosure:
|
||||
qButton->setMinimumWidth(13);
|
||||
qButton->setFixedHeight(13);
|
||||
qButton->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Fixed);
|
||||
break;
|
||||
case QButton::Circular:
|
||||
qButton->setMinimumSize(16, 16);
|
||||
qButton->setMaximumHeight(40);
|
||||
qButton->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum);
|
||||
break;
|
||||
case QButton::HelpButton:
|
||||
case QButton::RoundedDisclosure:
|
||||
qButton->setMinimumWidth(22);
|
||||
qButton->setFixedHeight(22);
|
||||
qButton->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Fixed);
|
||||
break;
|
||||
#ifdef __MAC_10_7
|
||||
case QButton::Inline:
|
||||
qButton->setMinimumWidth(10);
|
||||
qButton->setFixedHeight(16);
|
||||
qButton->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Fixed);
|
||||
break;
|
||||
#endif
|
||||
}
|
||||
|
||||
switch(bezelStyle) {
|
||||
case QButton::Recessed:
|
||||
[nsButton setButtonType:NSPushOnPushOffButton];
|
||||
case QButton::Disclosure:
|
||||
[nsButton setButtonType:NSOnOffButton];
|
||||
default:
|
||||
[nsButton setButtonType:NSMomentaryPushInButton];
|
||||
}
|
||||
|
||||
[nsButton setBezelStyle:(__bridge NSBezelStyle)bezelStyle];
|
||||
}
|
||||
|
||||
void clicked()
|
||||
{
|
||||
emit qButton->clicked(qButton->isChecked());
|
||||
}
|
||||
|
||||
~QButtonPrivate() {
|
||||
[[nsButton target] release];
|
||||
[nsButton setTarget:nil];
|
||||
}
|
||||
|
||||
QButton *qButton;
|
||||
NSButton *nsButton;
|
||||
};
|
||||
|
||||
@interface QButtonTarget : NSObject
|
||||
{
|
||||
@public
|
||||
QPointer<QButtonPrivate> pimpl;
|
||||
}
|
||||
-(void)clicked;
|
||||
@end
|
||||
|
||||
@implementation QButtonTarget
|
||||
-(void)clicked {
|
||||
Q_ASSERT(pimpl);
|
||||
if (pimpl)
|
||||
pimpl->clicked();
|
||||
}
|
||||
@end
|
||||
|
||||
QButton::QButton(QWidget *parent, BezelStyle bezelStyle) : QWidget(parent)
|
||||
{
|
||||
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
|
||||
|
||||
NSButton *button = [[NSButton alloc] init];
|
||||
pimpl = new QButtonPrivate(this, button, bezelStyle);
|
||||
|
||||
QButtonTarget *target = [[QButtonTarget alloc] init];
|
||||
target->pimpl = pimpl;
|
||||
[button setTarget:target];
|
||||
|
||||
[button setAction:@selector(clicked)];
|
||||
|
||||
setupLayout(button, this);
|
||||
|
||||
[button release];
|
||||
|
||||
[pool drain];
|
||||
}
|
||||
|
||||
void QButton::setText(const QString &text)
|
||||
{
|
||||
Q_ASSERT(pimpl);
|
||||
if (!pimpl)
|
||||
return;
|
||||
|
||||
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
|
||||
[pimpl->nsButton setTitle:fromQString(text)];
|
||||
[pool drain];
|
||||
}
|
||||
|
||||
void QButton::setImage(const QPixmap &image)
|
||||
{
|
||||
Q_ASSERT(pimpl);
|
||||
if (pimpl)
|
||||
[pimpl->nsButton setImage:fromQPixmap(image)];
|
||||
}
|
||||
|
||||
void QButton::setChecked(bool checked)
|
||||
{
|
||||
Q_ASSERT(pimpl);
|
||||
if (pimpl)
|
||||
[pimpl->nsButton setState:checked];
|
||||
}
|
||||
|
||||
void QButton::setCheckable(bool checkable)
|
||||
{
|
||||
const NSInteger cellMask = checkable ? NSChangeBackgroundCellMask : NSNoCellMask;
|
||||
|
||||
Q_ASSERT(pimpl);
|
||||
if (pimpl)
|
||||
[[pimpl->nsButton cell] setShowsStateBy:cellMask];
|
||||
}
|
||||
|
||||
bool QButton::isChecked()
|
||||
{
|
||||
Q_ASSERT(pimpl);
|
||||
if (!pimpl)
|
||||
return false;
|
||||
|
||||
return [pimpl->nsButton state];
|
||||
}
|
||||
89
3rdparty/qocoa/qbutton_nonmac.cpp
vendored
89
3rdparty/qocoa/qbutton_nonmac.cpp
vendored
@@ -1,89 +0,0 @@
|
||||
/*
|
||||
Copyright (C) 2011 by Mike McQuaid
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "qbutton.h"
|
||||
|
||||
#include <QToolBar>
|
||||
#include <QToolButton>
|
||||
#include <QPushButton>
|
||||
#include <QVBoxLayout>
|
||||
|
||||
class QButtonPrivate : public QObject
|
||||
{
|
||||
public:
|
||||
QButtonPrivate(QButton *button, QAbstractButton *abstractButton)
|
||||
: QObject(button), abstractButton(abstractButton) {}
|
||||
QPointer<QAbstractButton> abstractButton;
|
||||
};
|
||||
|
||||
QButton::QButton(QWidget *parent, BezelStyle) : QWidget(parent)
|
||||
{
|
||||
QAbstractButton *button = 0;
|
||||
if (qobject_cast<QToolBar*>(parent))
|
||||
button = new QToolButton(this);
|
||||
else
|
||||
button = new QPushButton(this);
|
||||
connect(button, SIGNAL(clicked()),
|
||||
this, SIGNAL(clicked()));
|
||||
pimpl = new QButtonPrivate(this, button);
|
||||
|
||||
QVBoxLayout *layout = new QVBoxLayout(this);
|
||||
layout->setMargin(0);
|
||||
layout->addWidget(button);
|
||||
}
|
||||
|
||||
void QButton::setText(const QString &text)
|
||||
{
|
||||
Q_ASSERT(pimpl);
|
||||
if (pimpl)
|
||||
pimpl->abstractButton->setText(text);
|
||||
}
|
||||
|
||||
void QButton::setImage(const QPixmap &image)
|
||||
{
|
||||
Q_ASSERT(pimpl);
|
||||
if (pimpl)
|
||||
pimpl->abstractButton->setIcon(image);
|
||||
}
|
||||
|
||||
void QButton::setChecked(bool checked)
|
||||
{
|
||||
Q_ASSERT(pimpl);
|
||||
if (pimpl)
|
||||
pimpl->abstractButton->setChecked(checked);
|
||||
}
|
||||
|
||||
void QButton::setCheckable(bool checkable)
|
||||
{
|
||||
Q_ASSERT(pimpl);
|
||||
if (pimpl)
|
||||
pimpl->abstractButton->setCheckable(checkable);
|
||||
}
|
||||
|
||||
bool QButton::isChecked()
|
||||
{
|
||||
Q_ASSERT(pimpl);
|
||||
if (!pimpl)
|
||||
return false;
|
||||
|
||||
return pimpl->abstractButton->isChecked();
|
||||
}
|
||||
29
3rdparty/qocoa/qprogressindicatorspinning.h
vendored
29
3rdparty/qocoa/qprogressindicatorspinning.h
vendored
@@ -1,29 +0,0 @@
|
||||
#ifndef QPROGRESSINDICATORSPINNING_H
|
||||
#define QPROGRESSINDICATORSPINNING_H
|
||||
|
||||
#include <QWidget>
|
||||
#include <QPointer>
|
||||
|
||||
class QProgressIndicatorSpinningPrivate;
|
||||
class QProgressIndicatorSpinning : public QWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
// Matches NSProgressIndicatorThickness
|
||||
enum Thickness {
|
||||
Default = 14,
|
||||
Small = 10,
|
||||
Large = 18,
|
||||
Aqua = 12
|
||||
};
|
||||
|
||||
explicit QProgressIndicatorSpinning(QWidget *parent,
|
||||
Thickness thickness = Default);
|
||||
public slots:
|
||||
void animate(bool animate = true);
|
||||
private:
|
||||
friend class QProgressIndicatorSpinningPrivate;
|
||||
QPointer<QProgressIndicatorSpinningPrivate> pimpl;
|
||||
};
|
||||
|
||||
#endif // QPROGRESSINDICATORSPINNING_H
|
||||
70
3rdparty/qocoa/qprogressindicatorspinning_mac.mm
vendored
70
3rdparty/qocoa/qprogressindicatorspinning_mac.mm
vendored
@@ -1,70 +0,0 @@
|
||||
/*
|
||||
Copyright (C) 2011 by Mike McQuaid
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "qprogressindicatorspinning.h"
|
||||
|
||||
#include "qocoa_mac.h"
|
||||
|
||||
#import "Foundation/NSAutoreleasePool.h"
|
||||
#import "AppKit/NSProgressIndicator.h"
|
||||
|
||||
class QProgressIndicatorSpinningPrivate : public QObject
|
||||
{
|
||||
public:
|
||||
QProgressIndicatorSpinningPrivate(QProgressIndicatorSpinning *qProgressIndicatorSpinning,
|
||||
NSProgressIndicator *nsProgressIndicator)
|
||||
: QObject(qProgressIndicatorSpinning), nsProgressIndicator(nsProgressIndicator) {}
|
||||
|
||||
NSProgressIndicator *nsProgressIndicator;
|
||||
};
|
||||
|
||||
QProgressIndicatorSpinning::QProgressIndicatorSpinning(QWidget *parent,
|
||||
Thickness thickness)
|
||||
: QWidget(parent)
|
||||
{
|
||||
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
|
||||
|
||||
NSProgressIndicator *progress = [[NSProgressIndicator alloc] init];
|
||||
[progress setStyle:NSProgressIndicatorSpinningStyle];
|
||||
|
||||
pimpl = new QProgressIndicatorSpinningPrivate(this, progress);
|
||||
|
||||
setupLayout(progress, this);
|
||||
|
||||
setFixedSize(thickness, thickness);
|
||||
|
||||
[progress release];
|
||||
|
||||
[pool drain];
|
||||
}
|
||||
|
||||
void QProgressIndicatorSpinning::animate(bool animate)
|
||||
{
|
||||
Q_ASSERT(pimpl);
|
||||
if (!pimpl)
|
||||
return;
|
||||
|
||||
if (animate)
|
||||
[pimpl->nsProgressIndicator startAnimation:nil];
|
||||
else
|
||||
[pimpl->nsProgressIndicator stopAnimation:nil];
|
||||
}
|
||||
@@ -1,72 +0,0 @@
|
||||
/*
|
||||
Copyright (C) 2011 by Mike McQuaid
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "qprogressindicatorspinning.h"
|
||||
|
||||
#include <QVBoxLayout>
|
||||
#include <QMovie>
|
||||
#include <QLabel>
|
||||
|
||||
class QProgressIndicatorSpinningPrivate : public QObject
|
||||
{
|
||||
public:
|
||||
QProgressIndicatorSpinningPrivate(QProgressIndicatorSpinning *qProgressIndicatorSpinning,
|
||||
QMovie *movie)
|
||||
: QObject(qProgressIndicatorSpinning), movie(movie) {}
|
||||
|
||||
QPointer<QMovie> movie;
|
||||
};
|
||||
|
||||
QProgressIndicatorSpinning::QProgressIndicatorSpinning(QWidget *parent,
|
||||
Thickness thickness)
|
||||
: QWidget(parent)
|
||||
{
|
||||
QVBoxLayout *layout = new QVBoxLayout(this);
|
||||
layout->setMargin(0);
|
||||
|
||||
QSize size(thickness, thickness);
|
||||
QMovie *movie = new QMovie(this);
|
||||
movie->setFileName(":/Qocoa/qprogressindicatorspinning_nonmac.gif");
|
||||
movie->setScaledSize(size);
|
||||
// Roughly match OSX speed.
|
||||
movie->setSpeed(200);
|
||||
pimpl = new QProgressIndicatorSpinningPrivate(this, movie);
|
||||
|
||||
QLabel *label = new QLabel(this);
|
||||
label->setMovie(movie);
|
||||
|
||||
layout->addWidget(label);
|
||||
setFixedSize(size);
|
||||
}
|
||||
|
||||
|
||||
void QProgressIndicatorSpinning::animate(bool animate)
|
||||
{
|
||||
Q_ASSERT(pimpl && pimpl->movie);
|
||||
if (!(pimpl && pimpl->movie))
|
||||
return;
|
||||
|
||||
if (animate)
|
||||
pimpl->movie->start();
|
||||
else
|
||||
pimpl->movie->stop();
|
||||
}
|
||||
BIN
3rdparty/qocoa/qprogressindicatorspinning_nonmac.gif
vendored
BIN
3rdparty/qocoa/qprogressindicatorspinning_nonmac.gif
vendored
Binary file not shown.
|
Before Width: | Height: | Size: 3.1 KiB |
@@ -1,5 +0,0 @@
|
||||
<RCC>
|
||||
<qresource prefix="/Qocoa">
|
||||
<file>qprogressindicatorspinning_nonmac.gif</file>
|
||||
</qresource>
|
||||
</RCC>
|
||||
7
3rdparty/qocoa/qsearchfield_nonmac.qrc
vendored
7
3rdparty/qocoa/qsearchfield_nonmac.qrc
vendored
@@ -1,7 +0,0 @@
|
||||
<RCC>
|
||||
<qresource prefix="/Qocoa">
|
||||
<file>qsearchfield_nonmac_clear.png</file>
|
||||
<file>qsearchfield_nonmac_magnifier_menu.png</file>
|
||||
<file>qsearchfield_nonmac_magnifier.png</file>
|
||||
</qresource>
|
||||
</RCC>
|
||||
BIN
3rdparty/qocoa/qsearchfield_nonmac_clear.png
vendored
BIN
3rdparty/qocoa/qsearchfield_nonmac_clear.png
vendored
Binary file not shown.
|
Before Width: | Height: | Size: 736 B |
BIN
3rdparty/qocoa/qsearchfield_nonmac_magnifier.png
vendored
BIN
3rdparty/qocoa/qsearchfield_nonmac_magnifier.png
vendored
Binary file not shown.
|
Before Width: | Height: | Size: 300 B |
Binary file not shown.
|
Before Width: | Height: | Size: 439 B |
18
3rdparty/singleapplication/CMakeLists.txt
vendored
18
3rdparty/singleapplication/CMakeLists.txt
vendored
@@ -1,5 +1,20 @@
|
||||
cmake_minimum_required(VERSION 2.8.11)
|
||||
|
||||
include(CheckIncludeFiles)
|
||||
include(CheckFunctionExists)
|
||||
|
||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall")
|
||||
set(CMAKE_CXX_STANDARD 11)
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} --std=c++11 -U__STRICT_ANSI__ -Wall -Woverloaded-virtual -Wno-sign-compare -fpermissive")
|
||||
|
||||
if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU")
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wextra -Wpedantic")
|
||||
endif()
|
||||
|
||||
if(CMAKE_VERSION VERSION_GREATER 3.0)
|
||||
check_function_exists(geteuid HAVE_GETEUID)
|
||||
check_function_exists(getpwuid HAVE_GETPWUID)
|
||||
endif()
|
||||
|
||||
set(SINGLEAPP-SOURCES singleapplication.cpp singleapplication_p.cpp)
|
||||
set(SINGLEAPP-MOC-HEADERS singleapplication.h singleapplication_p.h)
|
||||
@@ -12,3 +27,6 @@ set(SINGLECOREAPP-MOC-HEADERS singlecoreapplication.h singlecoreapplication_p.h)
|
||||
QT5_WRAP_CPP(SINGLECOREAPP-SOURCES-MOC ${SINGLECOREAPP-MOC-HEADERS})
|
||||
ADD_LIBRARY(singlecoreapplication STATIC ${SINGLECOREAPP-SOURCES} ${SINGLECOREAPP-SOURCES-MOC})
|
||||
target_link_libraries(singlecoreapplication Qt5::Core Qt5::Widgets Qt5::Network)
|
||||
|
||||
configure_file(config.h.in "${CMAKE_CURRENT_BINARY_DIR}/config.h")
|
||||
include_directories(${CMAKE_CURRENT_BINARY_DIR})
|
||||
|
||||
2
3rdparty/singleapplication/config.h.in
vendored
Normal file
2
3rdparty/singleapplication/config.h.in
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
#cmakedefine HAVE_GETEUID
|
||||
#cmakedefine HAVE_GETPWUID
|
||||
225
3rdparty/singleapplication/singleapplication.cpp
vendored
225
3rdparty/singleapplication/singleapplication.cpp
vendored
@@ -20,156 +20,165 @@
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
// THE SOFTWARE.
|
||||
|
||||
#include <QApplication>
|
||||
#include <QtCore/QTime>
|
||||
#include <QtCore/QThread>
|
||||
#include <QtCore/QDateTime>
|
||||
#include <QtCore/QByteArray>
|
||||
#include <QtCore/QSharedMemory>
|
||||
//
|
||||
// W A R N I N G !!!
|
||||
// -----------------
|
||||
//
|
||||
// This is a modified version of SingleApplication,
|
||||
// The original version is at:
|
||||
//
|
||||
// https://github.com/itay-grudev/SingleApplication
|
||||
//
|
||||
//
|
||||
|
||||
#include <QtGlobal>
|
||||
#include <QCoreApplication>
|
||||
#include <QThread>
|
||||
#include <QSharedMemory>
|
||||
#include <QByteArray>
|
||||
#include <QDateTime>
|
||||
#include <QTime>
|
||||
|
||||
#include "singleapplication.h"
|
||||
#include "singleapplication_p.h"
|
||||
|
||||
/**
|
||||
* @brief Constructor. Checks and fires up LocalServer or closes the program
|
||||
* if another instance already exists
|
||||
* @brief Constructor.
|
||||
* Checks and fires up LocalServer or closes the program if another instance already exists
|
||||
* @param argc
|
||||
* @param argv
|
||||
* @param {bool} allowSecondaryInstances
|
||||
*/
|
||||
SingleApplication::SingleApplication( int &argc, char *argv[], bool allowSecondary, Options options, int timeout )
|
||||
: app_t( argc, argv ), d_ptr( new SingleApplicationPrivate( this ) )
|
||||
{
|
||||
Q_D(SingleApplication);
|
||||
SingleApplication::SingleApplication(int &argc, char *argv[], bool allowSecondary, Options options, int timeout)
|
||||
: app_t(argc, argv), d_ptr(new SingleApplicationPrivate(this)) {
|
||||
|
||||
// Store the current mode of the program
|
||||
d->options = options;
|
||||
Q_D(SingleApplication);
|
||||
|
||||
// Generating an application ID used for identifying the shared memory
|
||||
// block and QLocalServer
|
||||
d->genBlockServerName();
|
||||
// Store the current mode of the program
|
||||
d->options = options;
|
||||
|
||||
// Generating an application ID used for identifying the shared memory block and QLocalServer
|
||||
d->genBlockServerName();
|
||||
|
||||
#ifdef Q_OS_UNIX
|
||||
// By explicitly attaching it and then deleting it we make sure that the
|
||||
// memory is deleted even after the process has crashed on Unix.
|
||||
d->memory = new QSharedMemory( d->blockServerName );
|
||||
d->memory->attach();
|
||||
delete d->memory;
|
||||
// By explicitly attaching it and then deleting it we make sure that the
|
||||
// memory is deleted even after the process has crashed on Unix.
|
||||
d->memory = new QSharedMemory(d->blockServerName);
|
||||
d->memory->attach();
|
||||
delete d->memory;
|
||||
#endif
|
||||
// Guarantee thread safe behaviour with a shared memory block.
|
||||
d->memory = new QSharedMemory( d->blockServerName );
|
||||
// Guarantee thread safe behaviour with a shared memory block.
|
||||
d->memory = new QSharedMemory(d->blockServerName);
|
||||
|
||||
// Create a shared memory block
|
||||
if( d->memory->create( sizeof( InstancesInfo ) ) ) {
|
||||
// Initialize the shared memory block
|
||||
d->memory->lock();
|
||||
d->initializeMemoryBlock();
|
||||
d->memory->unlock();
|
||||
} else {
|
||||
// Attempt to attach to the memory segment
|
||||
if( ! d->memory->attach() ) {
|
||||
qCritical() << "SingleApplication: Unable to attach to shared memory block.";
|
||||
qCritical() << d->memory->errorString();
|
||||
delete d;
|
||||
::exit( EXIT_FAILURE );
|
||||
}
|
||||
// Create a shared memory block
|
||||
if (d->memory->create(sizeof(InstancesInfo))) {
|
||||
// Initialize the shared memory block
|
||||
d->memory->lock();
|
||||
d->initializeMemoryBlock();
|
||||
d->memory->unlock();
|
||||
}
|
||||
else {
|
||||
// Attempt to attach to the memory segment
|
||||
if (! d->memory->attach()) {
|
||||
qCritical() << "SingleApplication: Unable to attach to shared memory block.";
|
||||
qCritical() << d->memory->errorString();
|
||||
delete d;
|
||||
::exit(EXIT_FAILURE);
|
||||
}
|
||||
}
|
||||
|
||||
InstancesInfo* inst = static_cast<InstancesInfo*>( d->memory->data() );
|
||||
QTime time;
|
||||
time.start();
|
||||
InstancesInfo* inst = static_cast<InstancesInfo*>(d->memory->data());
|
||||
QTime time;
|
||||
time.start();
|
||||
|
||||
// Make sure the shared memory block is initialised and in consistent state
|
||||
while( true ) {
|
||||
d->memory->lock();
|
||||
// Make sure the shared memory block is initialised and in consistent state
|
||||
while (true) {
|
||||
d->memory->lock();
|
||||
|
||||
if( d->blockChecksum() == inst->checksum ) break;
|
||||
if (d->blockChecksum() == inst->checksum) break;
|
||||
|
||||
if( time.elapsed() > 5000 ) {
|
||||
qWarning() << "SingleApplication: Shared memory block has been in an inconsistent state from more than 5s. Assuming primary instance failure.";
|
||||
d->initializeMemoryBlock();
|
||||
}
|
||||
|
||||
d->memory->unlock();
|
||||
|
||||
// Random sleep here limits the probability of a collision between two racing apps
|
||||
qsrand( QDateTime::currentMSecsSinceEpoch() % std::numeric_limits<uint>::max() );
|
||||
QThread::sleep( 8 + static_cast <unsigned long>( static_cast <float>( qrand() ) / RAND_MAX * 10 ) );
|
||||
}
|
||||
|
||||
if( inst->primary == false) {
|
||||
d->startPrimary();
|
||||
d->memory->unlock();
|
||||
return;
|
||||
}
|
||||
|
||||
// Check if another instance can be started
|
||||
if( allowSecondary ) {
|
||||
inst->secondary += 1;
|
||||
inst->checksum = d->blockChecksum();
|
||||
d->instanceNumber = inst->secondary;
|
||||
d->startSecondary();
|
||||
if( d->options & Mode::SecondaryNotification ) {
|
||||
d->connectToPrimary( timeout, SingleApplicationPrivate::SecondaryInstance );
|
||||
}
|
||||
d->memory->unlock();
|
||||
return;
|
||||
if (time.elapsed() > 5000) {
|
||||
qWarning() << "SingleApplication: Shared memory block has been in an inconsistent state from more than 5s. Assuming primary instance failure.";
|
||||
d->initializeMemoryBlock();
|
||||
}
|
||||
|
||||
d->memory->unlock();
|
||||
|
||||
d->connectToPrimary( timeout, SingleApplicationPrivate::NewInstance );
|
||||
// Random sleep here limits the probability of a collision between two racing apps
|
||||
qsrand(QDateTime::currentMSecsSinceEpoch() % std::numeric_limits<uint>::max());
|
||||
QThread::sleep(8 + static_cast <unsigned long>(static_cast <float>(qrand()) / RAND_MAX * 10));
|
||||
}
|
||||
|
||||
delete d;
|
||||
if (inst->primary == false) {
|
||||
d->startPrimary();
|
||||
d->memory->unlock();
|
||||
return;
|
||||
}
|
||||
|
||||
// Check if another instance can be started
|
||||
if (allowSecondary) {
|
||||
inst->secondary += 1;
|
||||
inst->checksum = d->blockChecksum();
|
||||
d->instanceNumber = inst->secondary;
|
||||
d->startSecondary();
|
||||
if (d->options & Mode::SecondaryNotification) {
|
||||
d->connectToPrimary(timeout, SingleApplicationPrivate::SecondaryInstance);
|
||||
}
|
||||
d->memory->unlock();
|
||||
return;
|
||||
}
|
||||
|
||||
d->memory->unlock();
|
||||
|
||||
d->connectToPrimary(timeout, SingleApplicationPrivate::NewInstance);
|
||||
|
||||
delete d;
|
||||
|
||||
::exit(EXIT_SUCCESS);
|
||||
|
||||
::exit( EXIT_SUCCESS );
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Destructor
|
||||
*/
|
||||
SingleApplication::~SingleApplication()
|
||||
{
|
||||
Q_D(SingleApplication);
|
||||
delete d;
|
||||
SingleApplication::~SingleApplication() {
|
||||
Q_D(SingleApplication);
|
||||
delete d;
|
||||
}
|
||||
|
||||
bool SingleApplication::isPrimary()
|
||||
{
|
||||
Q_D(SingleApplication);
|
||||
return d->server != nullptr;
|
||||
bool SingleApplication::isPrimary() {
|
||||
Q_D(SingleApplication);
|
||||
return d->server != nullptr;
|
||||
}
|
||||
|
||||
bool SingleApplication::isSecondary()
|
||||
{
|
||||
Q_D(SingleApplication);
|
||||
return d->server == nullptr;
|
||||
bool SingleApplication::isSecondary() {
|
||||
Q_D(SingleApplication);
|
||||
return d->server == nullptr;
|
||||
}
|
||||
|
||||
quint32 SingleApplication::instanceId()
|
||||
{
|
||||
Q_D(SingleApplication);
|
||||
return d->instanceNumber;
|
||||
quint32 SingleApplication::instanceId() {
|
||||
Q_D(SingleApplication);
|
||||
return d->instanceNumber;
|
||||
}
|
||||
|
||||
qint64 SingleApplication::primaryPid()
|
||||
{
|
||||
Q_D(SingleApplication);
|
||||
return d->primaryPid();
|
||||
qint64 SingleApplication::primaryPid() {
|
||||
Q_D(SingleApplication);
|
||||
return d->primaryPid();
|
||||
}
|
||||
|
||||
bool SingleApplication::sendMessage( QByteArray message, int timeout )
|
||||
{
|
||||
Q_D(SingleApplication);
|
||||
bool SingleApplication::sendMessage(QByteArray message, int timeout) {
|
||||
|
||||
// Nobody to connect to
|
||||
if( isPrimary() ) return false;
|
||||
Q_D(SingleApplication);
|
||||
|
||||
// Make sure the socket is connected
|
||||
d->connectToPrimary( timeout, SingleApplicationPrivate::Reconnect );
|
||||
// Nobody to connect to
|
||||
if (isPrimary()) return false;
|
||||
|
||||
// Make sure the socket is connected
|
||||
d->connectToPrimary(timeout, SingleApplicationPrivate::Reconnect);
|
||||
|
||||
d->socket->write(message);
|
||||
bool dataWritten = d->socket->waitForBytesWritten(timeout);
|
||||
d->socket->flush();
|
||||
return dataWritten;
|
||||
|
||||
d->socket->write( message );
|
||||
bool dataWritten = d->socket->flush();
|
||||
d->socket->waitForBytesWritten( timeout );
|
||||
return dataWritten;
|
||||
}
|
||||
|
||||
171
3rdparty/singleapplication/singleapplication.h
vendored
171
3rdparty/singleapplication/singleapplication.h
vendored
@@ -20,12 +20,23 @@
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
// THE SOFTWARE.
|
||||
|
||||
//
|
||||
// W A R N I N G !!!
|
||||
// -----------------
|
||||
//
|
||||
// This is a modified version of SingleApplication,
|
||||
// The original version is at:
|
||||
//
|
||||
// https://github.com/itay-grudev/SingleApplication
|
||||
//
|
||||
//
|
||||
|
||||
#ifndef SINGLEAPPLICATION_H
|
||||
#define SINGLEAPPLICATION_H
|
||||
|
||||
#include <QtCore/QtGlobal>
|
||||
#include <QtGlobal>
|
||||
#include <QApplication>
|
||||
#include <QtNetwork/QLocalSocket>
|
||||
#include <QLocalSocket>
|
||||
|
||||
class SingleApplicationPrivate;
|
||||
|
||||
@@ -34,95 +45,95 @@ class SingleApplicationPrivate;
|
||||
* Application
|
||||
* @see QCoreApplication
|
||||
*/
|
||||
class SingleApplication : public QApplication
|
||||
{
|
||||
Q_OBJECT
|
||||
class SingleApplication : public QApplication {
|
||||
Q_OBJECT
|
||||
|
||||
typedef QApplication app_t;
|
||||
typedef QApplication app_t;
|
||||
|
||||
public:
|
||||
/**
|
||||
* @brief Mode of operation of SingleApplication.
|
||||
* Whether the block should be user-wide or system-wide and whether the
|
||||
* primary instance should be notified when a secondary instance had been
|
||||
* started.
|
||||
* @note Operating system can restrict the shared memory blocks to the same
|
||||
* user, in which case the User/System modes will have no effect and the
|
||||
* block will be user wide.
|
||||
* @enum
|
||||
*/
|
||||
enum Mode {
|
||||
User = 1 << 0,
|
||||
System = 1 << 1,
|
||||
SecondaryNotification = 1 << 2,
|
||||
ExcludeAppVersion = 1 << 3,
|
||||
ExcludeAppPath = 1 << 4
|
||||
};
|
||||
Q_DECLARE_FLAGS(Options, Mode)
|
||||
/**
|
||||
* @brief Mode of operation of SingleApplication.
|
||||
* Whether the block should be user-wide or system-wide and whether the
|
||||
* primary instance should be notified when a secondary instance had been
|
||||
* started.
|
||||
* @note Operating system can restrict the shared memory blocks to the same
|
||||
* user, in which case the User/System modes will have no effect and the
|
||||
* block will be user wide.
|
||||
* @enum
|
||||
*/
|
||||
enum Mode {
|
||||
User = 1 << 0,
|
||||
System = 1 << 1,
|
||||
SecondaryNotification = 1 << 2,
|
||||
ExcludeAppVersion = 1 << 3,
|
||||
ExcludeAppPath = 1 << 4
|
||||
};
|
||||
Q_DECLARE_FLAGS(Options, Mode)
|
||||
|
||||
/**
|
||||
* @brief Intitializes a SingleApplication instance with argc command line
|
||||
* arguments in argv
|
||||
* @arg {int &} argc - Number of arguments in argv
|
||||
* @arg {const char *[]} argv - Supplied command line arguments
|
||||
* @arg {bool} allowSecondary - Whether to start the instance as secondary
|
||||
* if there is already a primary instance.
|
||||
* @arg {Mode} mode - Whether for the SingleApplication block to be applied
|
||||
* User wide or System wide.
|
||||
* @arg {int} timeout - Timeout to wait in miliseconds.
|
||||
* @note argc and argv may be changed as Qt removes arguments that it
|
||||
* recognizes
|
||||
* @note Mode::SecondaryNotification only works if set on both the primary
|
||||
* instance and the secondary instance.
|
||||
* @note The timeout is just a hint for the maximum time of blocking
|
||||
* operations. It does not guarantee that the SingleApplication
|
||||
* initialisation will be completed in given time, though is a good hint.
|
||||
* Usually 4*timeout would be the worst case (fail) scenario.
|
||||
* @see See the corresponding QAPPLICATION_CLASS constructor for reference
|
||||
*/
|
||||
explicit SingleApplication( int &argc, char *argv[], bool allowSecondary = false, Options options = Mode::User, int timeout = 1000 );
|
||||
~SingleApplication();
|
||||
/**
|
||||
* @brief Intitializes a SingleApplication instance with argc command line
|
||||
* arguments in argv
|
||||
* @arg {int &} argc - Number of arguments in argv
|
||||
* @arg {const char *[]} argv - Supplied command line arguments
|
||||
* @arg {bool} allowSecondary - Whether to start the instance as secondary
|
||||
* if there is already a primary instance.
|
||||
* @arg {Mode} mode - Whether for the SingleApplication block to be applied
|
||||
* User wide or System wide.
|
||||
* @arg {int} timeout - Timeout to wait in milliseconds.
|
||||
* @note argc and argv may be changed as Qt removes arguments that it
|
||||
* recognizes
|
||||
* @note Mode::SecondaryNotification only works if set on both the primary
|
||||
* instance and the secondary instance.
|
||||
* @note The timeout is just a hint for the maximum time of blocking
|
||||
* operations. It does not guarantee that the SingleApplication
|
||||
* initialisation will be completed in given time, though is a good hint.
|
||||
* Usually 4*timeout would be the worst case (fail) scenario.
|
||||
* @see See the corresponding QAPPLICATION_CLASS constructor for reference
|
||||
*/
|
||||
explicit SingleApplication( int &argc, char *argv[], bool allowSecondary = false, Options options = Mode::User, int timeout = 1000 );
|
||||
~SingleApplication();
|
||||
|
||||
/**
|
||||
* @brief Returns if the instance is the primary instance
|
||||
* @returns {bool}
|
||||
*/
|
||||
bool isPrimary();
|
||||
/**
|
||||
* @brief Returns if the instance is the primary instance
|
||||
* @returns {bool}
|
||||
*/
|
||||
bool isPrimary();
|
||||
|
||||
/**
|
||||
* @brief Returns if the instance is a secondary instance
|
||||
* @returns {bool}
|
||||
*/
|
||||
bool isSecondary();
|
||||
/**
|
||||
* @brief Returns if the instance is a secondary instance
|
||||
* @returns {bool}
|
||||
*/
|
||||
bool isSecondary();
|
||||
|
||||
/**
|
||||
* @brief Returns a unique identifier for the current instance
|
||||
* @returns {qint32}
|
||||
*/
|
||||
quint32 instanceId();
|
||||
/**
|
||||
* @brief Returns a unique identifier for the current instance
|
||||
* @returns {qint32}
|
||||
*/
|
||||
quint32 instanceId();
|
||||
|
||||
/**
|
||||
* @brief Returns the process ID (PID) of the primary instance
|
||||
* @returns {qint64}
|
||||
*/
|
||||
qint64 primaryPid();
|
||||
/**
|
||||
* @brief Returns the process ID (PID) of the primary instance
|
||||
* @returns {qint64}
|
||||
*/
|
||||
qint64 primaryPid();
|
||||
|
||||
/**
|
||||
* @brief Sends a message to the primary instance. Returns true on success.
|
||||
* @param {int} timeout - Timeout for connecting
|
||||
* @returns {bool}
|
||||
* @note sendMessage() will return false if invoked from the primary
|
||||
* instance.
|
||||
*/
|
||||
bool sendMessage( QByteArray message, int timeout = 100 );
|
||||
/**
|
||||
* @brief Sends a message to the primary instance. Returns true on success.
|
||||
* @param {int} timeout - Timeout for connecting
|
||||
* @returns {bool}
|
||||
* @note sendMessage() will return false if invoked from the primary
|
||||
* instance.
|
||||
*/
|
||||
bool sendMessage( QByteArray message, int timeout = 1000 );
|
||||
|
||||
Q_SIGNALS:
|
||||
void instanceStarted();
|
||||
void receivedMessage( quint32 instanceId, QByteArray message );
|
||||
signals:
|
||||
void instanceStarted();
|
||||
void receivedMessage( quint32 instanceId, QByteArray message );
|
||||
|
||||
private:
|
||||
SingleApplicationPrivate *d_ptr;
|
||||
Q_DECLARE_PRIVATE(SingleApplication)
|
||||
|
||||
private:
|
||||
SingleApplicationPrivate *d_ptr;
|
||||
Q_DECLARE_PRIVATE(SingleApplication)
|
||||
};
|
||||
|
||||
Q_DECLARE_OPERATORS_FOR_FLAGS(SingleApplication::Options)
|
||||
|
||||
585
3rdparty/singleapplication/singleapplication_p.cpp
vendored
585
3rdparty/singleapplication/singleapplication_p.cpp
vendored
@@ -24,381 +24,386 @@
|
||||
// W A R N I N G !!!
|
||||
// -----------------
|
||||
//
|
||||
// This file is not part of the SingleApplication API. It is used purely as an
|
||||
// implementation detail. This header file may change from version to
|
||||
// version without notice, or may even be removed.
|
||||
// This is a modified version of SingleApplication,
|
||||
// The original version is at:
|
||||
//
|
||||
// https://github.com/itay-grudev/SingleApplication
|
||||
//
|
||||
//
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <QtGlobal>
|
||||
|
||||
#include <cstdlib>
|
||||
#include <cstddef>
|
||||
|
||||
#include <QtCore/QDir>
|
||||
#include <QtCore/QProcess>
|
||||
#include <QtCore/QByteArray>
|
||||
#include <QtCore/QSemaphore>
|
||||
#include <QtCore/QDataStream>
|
||||
#include <QtCore/QStandardPaths>
|
||||
#include <QtCore/QCryptographicHash>
|
||||
#include <QtNetwork/QLocalServer>
|
||||
#include <QtNetwork/QLocalSocket>
|
||||
#ifdef Q_OS_UNIX
|
||||
# include <unistd.h>
|
||||
# include <sys/types.h>
|
||||
# include <pwd.h>
|
||||
#endif
|
||||
|
||||
#include <QDir>
|
||||
#include <QByteArray>
|
||||
#include <QDataStream>
|
||||
#include <QCryptographicHash>
|
||||
#include <QLocalServer>
|
||||
#include <QLocalSocket>
|
||||
|
||||
#include "singleapplication.h"
|
||||
#include "singleapplication_p.h"
|
||||
|
||||
#ifdef Q_OS_WIN
|
||||
#include <windows.h>
|
||||
#include <lmcons.h>
|
||||
# include <windows.h>
|
||||
# include <lmcons.h>
|
||||
#endif
|
||||
|
||||
SingleApplicationPrivate::SingleApplicationPrivate( SingleApplication *q_ptr )
|
||||
: q_ptr( q_ptr )
|
||||
{
|
||||
server = nullptr;
|
||||
socket = nullptr;
|
||||
memory = nullptr;
|
||||
instanceNumber = -1;
|
||||
}
|
||||
SingleApplicationPrivate::SingleApplicationPrivate(SingleApplication *q_ptr)
|
||||
: q_ptr(q_ptr),
|
||||
memory(nullptr),
|
||||
socket(nullptr),
|
||||
server(nullptr),
|
||||
instanceNumber(-1)
|
||||
{}
|
||||
|
||||
SingleApplicationPrivate::~SingleApplicationPrivate()
|
||||
{
|
||||
if( socket != nullptr ) {
|
||||
socket->close();
|
||||
delete socket;
|
||||
}
|
||||
SingleApplicationPrivate::~SingleApplicationPrivate() {
|
||||
|
||||
memory->lock();
|
||||
InstancesInfo* inst = static_cast<InstancesInfo*>(memory->data());
|
||||
if( server != nullptr ) {
|
||||
server->close();
|
||||
delete server;
|
||||
inst->primary = false;
|
||||
inst->primaryPid = -1;
|
||||
inst->checksum = blockChecksum();
|
||||
}
|
||||
memory->unlock();
|
||||
if (socket != nullptr) {
|
||||
socket->close();
|
||||
delete socket;
|
||||
}
|
||||
|
||||
delete memory;
|
||||
}
|
||||
|
||||
void SingleApplicationPrivate::genBlockServerName()
|
||||
{
|
||||
QCryptographicHash appData( QCryptographicHash::Sha256 );
|
||||
appData.addData( "SingleApplication", 17 );
|
||||
appData.addData( SingleApplication::app_t::applicationName().toUtf8() );
|
||||
appData.addData( SingleApplication::app_t::organizationName().toUtf8() );
|
||||
appData.addData( SingleApplication::app_t::organizationDomain().toUtf8() );
|
||||
|
||||
if( ! (options & SingleApplication::Mode::ExcludeAppVersion) ) {
|
||||
appData.addData( SingleApplication::app_t::applicationVersion().toUtf8() );
|
||||
}
|
||||
|
||||
if( ! (options & SingleApplication::Mode::ExcludeAppPath) ) {
|
||||
#ifdef Q_OS_WIN
|
||||
appData.addData( SingleApplication::app_t::applicationFilePath().toLower().toUtf8() );
|
||||
#else
|
||||
appData.addData( SingleApplication::app_t::applicationFilePath().toUtf8() );
|
||||
#endif
|
||||
}
|
||||
|
||||
// User level block requires a user specific data in the hash
|
||||
if( options & SingleApplication::Mode::User ) {
|
||||
#ifdef Q_OS_WIN
|
||||
wchar_t username [ UNLEN + 1 ];
|
||||
// Specifies size of the buffer on input
|
||||
DWORD usernameLength = UNLEN + 1;
|
||||
if( GetUserNameW( username, &usernameLength ) ) {
|
||||
appData.addData( QString::fromWCharArray(username).toUtf8() );
|
||||
} else {
|
||||
appData.addData( QStandardPaths::standardLocations( QStandardPaths::HomeLocation ).join("").toUtf8() );
|
||||
}
|
||||
#endif
|
||||
#ifdef Q_OS_UNIX
|
||||
QProcess process;
|
||||
process.start( "whoami" );
|
||||
if( process.waitForFinished( 100 ) &&
|
||||
process.exitCode() == QProcess::NormalExit) {
|
||||
appData.addData( process.readLine() );
|
||||
} else {
|
||||
appData.addData(
|
||||
QDir(
|
||||
QStandardPaths::standardLocations( QStandardPaths::HomeLocation ).first()
|
||||
).absolutePath().toUtf8()
|
||||
);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
// Replace the backslash in RFC 2045 Base64 [a-zA-Z0-9+/=] to comply with
|
||||
// server naming requirements.
|
||||
blockServerName = appData.result().toBase64().replace("/", "_");
|
||||
}
|
||||
|
||||
void SingleApplicationPrivate::initializeMemoryBlock()
|
||||
{
|
||||
InstancesInfo* inst = static_cast<InstancesInfo*>( memory->data() );
|
||||
memory->lock();
|
||||
InstancesInfo* inst = static_cast<InstancesInfo*>(memory->data());
|
||||
if (server != nullptr) {
|
||||
server->close();
|
||||
delete server;
|
||||
inst->primary = false;
|
||||
inst->secondary = 0;
|
||||
inst->primaryPid = -1;
|
||||
inst->checksum = blockChecksum();
|
||||
}
|
||||
memory->unlock();
|
||||
|
||||
delete memory;
|
||||
|
||||
}
|
||||
|
||||
void SingleApplicationPrivate::startPrimary()
|
||||
{
|
||||
Q_Q(SingleApplication);
|
||||
void SingleApplicationPrivate::genBlockServerName() {
|
||||
|
||||
// Successful creation means that no main process exists
|
||||
// So we start a QLocalServer to listen for connections
|
||||
QLocalServer::removeServer( blockServerName );
|
||||
server = new QLocalServer();
|
||||
QCryptographicHash appData(QCryptographicHash::Sha256);
|
||||
appData.addData("SingleApplication", 17);
|
||||
appData.addData(SingleApplication::app_t::applicationName().toUtf8());
|
||||
appData.addData(SingleApplication::app_t::organizationName().toUtf8());
|
||||
appData.addData(SingleApplication::app_t::organizationDomain().toUtf8());
|
||||
|
||||
// Restrict access to the socket according to the
|
||||
// SingleApplication::Mode::User flag on User level or no restrictions
|
||||
if( options & SingleApplication::Mode::User ) {
|
||||
server->setSocketOptions( QLocalServer::UserAccessOption );
|
||||
} else {
|
||||
server->setSocketOptions( QLocalServer::WorldAccessOption );
|
||||
if (!(options & SingleApplication::Mode::ExcludeAppVersion)) {
|
||||
appData.addData(SingleApplication::app_t::applicationVersion().toUtf8());
|
||||
}
|
||||
|
||||
if (! (options & SingleApplication::Mode::ExcludeAppPath)) {
|
||||
#ifdef Q_OS_WIN
|
||||
appData.addData(SingleApplication::app_t::applicationFilePath().toLower().toUtf8());
|
||||
#else
|
||||
appData.addData(SingleApplication::app_t::applicationFilePath().toUtf8());
|
||||
#endif
|
||||
}
|
||||
|
||||
// User level block requires a user specific data in the hash
|
||||
if (options & SingleApplication::Mode::User) {
|
||||
#ifdef Q_OS_UNIX
|
||||
QByteArray username;
|
||||
#if defined(HAVE_GETEUID) && defined(HAVE_GETPWUID)
|
||||
uid_t uid = geteuid();
|
||||
if (uid != -1) {
|
||||
struct passwd *pw = getpwuid(uid);
|
||||
if (pw) {
|
||||
username = pw->pw_name;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
if (username.isEmpty()) username = qgetenv("USER");
|
||||
appData.addData(username);
|
||||
#endif
|
||||
#ifdef Q_OS_WIN
|
||||
wchar_t username [ UNLEN + 1 ];
|
||||
// Specifies size of the buffer on input
|
||||
DWORD usernameLength = UNLEN + 1;
|
||||
if (GetUserNameW(username, &usernameLength)) {
|
||||
appData.addData(QString::fromWCharArray(username).toUtf8());
|
||||
}
|
||||
else {
|
||||
appData.addData(qgetenv("USERNAME"));
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
server->listen( blockServerName );
|
||||
QObject::connect(
|
||||
server,
|
||||
&QLocalServer::newConnection,
|
||||
this,
|
||||
&SingleApplicationPrivate::slotConnectionEstablished
|
||||
);
|
||||
// Replace the backslash in RFC 2045 Base64 [a-zA-Z0-9+/=] to comply with server naming requirements.
|
||||
blockServerName = appData.result().toBase64().replace("/", "_");
|
||||
|
||||
// Reset the number of connections
|
||||
InstancesInfo* inst = static_cast <InstancesInfo*>( memory->data() );
|
||||
|
||||
inst->primary = true;
|
||||
inst->primaryPid = q->applicationPid();
|
||||
inst->checksum = blockChecksum();
|
||||
|
||||
instanceNumber = 0;
|
||||
}
|
||||
|
||||
void SingleApplicationPrivate::startSecondary()
|
||||
{
|
||||
void SingleApplicationPrivate::initializeMemoryBlock() {
|
||||
|
||||
InstancesInfo* inst = static_cast<InstancesInfo*>(memory->data());
|
||||
inst->primary = false;
|
||||
inst->secondary = 0;
|
||||
inst->primaryPid = -1;
|
||||
inst->checksum = blockChecksum();
|
||||
|
||||
}
|
||||
|
||||
void SingleApplicationPrivate::connectToPrimary( int msecs, ConnectionType connectionType )
|
||||
{
|
||||
// Connect to the Local Server of the Primary Instance if not already
|
||||
// connected.
|
||||
if( socket == nullptr ) {
|
||||
socket = new QLocalSocket();
|
||||
}
|
||||
void SingleApplicationPrivate::startPrimary() {
|
||||
|
||||
// If already connected - we are done;
|
||||
if( socket->state() == QLocalSocket::ConnectedState )
|
||||
return;
|
||||
Q_Q(SingleApplication);
|
||||
|
||||
// If not connect
|
||||
if( socket->state() == QLocalSocket::UnconnectedState ||
|
||||
socket->state() == QLocalSocket::ClosingState ) {
|
||||
socket->connectToServer( blockServerName );
|
||||
}
|
||||
// Successful creation means that no main process exists
|
||||
// So we start a QLocalServer to listen for connections
|
||||
QLocalServer::removeServer(blockServerName);
|
||||
server = new QLocalServer();
|
||||
|
||||
// Wait for being connected
|
||||
if( socket->state() == QLocalSocket::ConnectingState ) {
|
||||
socket->waitForConnected( msecs );
|
||||
}
|
||||
// Restrict access to the socket according to the
|
||||
// SingleApplication::Mode::User flag on User level or no restrictions
|
||||
if (options & SingleApplication::Mode::User) {
|
||||
server->setSocketOptions(QLocalServer::UserAccessOption);
|
||||
}
|
||||
else {
|
||||
server->setSocketOptions(QLocalServer::WorldAccessOption);
|
||||
}
|
||||
|
||||
// Initialisation message according to the SingleApplication protocol
|
||||
if( socket->state() == QLocalSocket::ConnectedState ) {
|
||||
// Notify the parent that a new instance had been started;
|
||||
QByteArray initMsg;
|
||||
QDataStream writeStream(&initMsg, QIODevice::WriteOnly);
|
||||
server->listen(blockServerName);
|
||||
QObject::connect(server, &QLocalServer::newConnection, this, &SingleApplicationPrivate::slotConnectionEstablished);
|
||||
|
||||
// Reset the number of connections
|
||||
InstancesInfo* inst = static_cast <InstancesInfo*>(memory->data());
|
||||
|
||||
inst->primary = true;
|
||||
inst->primaryPid = q->applicationPid();
|
||||
inst->checksum = blockChecksum();
|
||||
|
||||
instanceNumber = 0;
|
||||
|
||||
}
|
||||
|
||||
void SingleApplicationPrivate::startSecondary() {}
|
||||
|
||||
void SingleApplicationPrivate::connectToPrimary(int msecs, ConnectionType connectionType) {
|
||||
|
||||
// Connect to the Local Server of the Primary Instance if not already connected.
|
||||
if (socket == nullptr) {
|
||||
socket = new QLocalSocket();
|
||||
}
|
||||
|
||||
// If already connected - we are done;
|
||||
if (socket->state() == QLocalSocket::ConnectedState)
|
||||
return;
|
||||
|
||||
// If not connect
|
||||
if (socket->state() == QLocalSocket::UnconnectedState ||
|
||||
socket->state() == QLocalSocket::ClosingState) {
|
||||
socket->connectToServer(blockServerName);
|
||||
}
|
||||
|
||||
// Wait for being connected
|
||||
if (socket->state() == QLocalSocket::ConnectingState) {
|
||||
socket->waitForConnected(msecs);
|
||||
}
|
||||
|
||||
// Initialisation message according to the SingleApplication protocol
|
||||
if (socket->state() == QLocalSocket::ConnectedState) {
|
||||
// Notify the parent that a new instance had been started;
|
||||
QByteArray initMsg;
|
||||
QDataStream writeStream(&initMsg, QIODevice::WriteOnly);
|
||||
|
||||
#if (QT_VERSION >= QT_VERSION_CHECK(5, 6, 0))
|
||||
writeStream.setVersion(QDataStream::Qt_5_6);
|
||||
writeStream.setVersion(QDataStream::Qt_5_6);
|
||||
#endif
|
||||
|
||||
writeStream << blockServerName.toLatin1();
|
||||
writeStream << static_cast<quint8>(connectionType);
|
||||
writeStream << instanceNumber;
|
||||
quint16 checksum = qChecksum(initMsg.constData(), static_cast<quint32>(initMsg.length()));
|
||||
writeStream << checksum;
|
||||
writeStream << blockServerName.toLatin1();
|
||||
writeStream << static_cast<quint8>(connectionType);
|
||||
writeStream << instanceNumber;
|
||||
quint16 checksum = qChecksum(initMsg.constData(), static_cast<quint32>(initMsg.length()));
|
||||
writeStream << checksum;
|
||||
|
||||
// The header indicates the message length that follows
|
||||
QByteArray header;
|
||||
QDataStream headerStream(&header, QIODevice::WriteOnly);
|
||||
// The header indicates the message length that follows
|
||||
QByteArray header;
|
||||
QDataStream headerStream(&header, QIODevice::WriteOnly);
|
||||
|
||||
#if (QT_VERSION >= QT_VERSION_CHECK(5, 6, 0))
|
||||
headerStream.setVersion(QDataStream::Qt_5_6);
|
||||
headerStream.setVersion(QDataStream::Qt_5_6);
|
||||
#endif
|
||||
headerStream << static_cast <quint64>( initMsg.length() );
|
||||
headerStream << static_cast <quint64>(initMsg.length());
|
||||
|
||||
socket->write(header);
|
||||
socket->write(initMsg);
|
||||
socket->flush();
|
||||
socket->waitForBytesWritten(msecs);
|
||||
}
|
||||
|
||||
socket->write( header );
|
||||
socket->write( initMsg );
|
||||
socket->flush();
|
||||
socket->waitForBytesWritten( msecs );
|
||||
}
|
||||
}
|
||||
|
||||
quint16 SingleApplicationPrivate::blockChecksum()
|
||||
{
|
||||
return qChecksum(
|
||||
static_cast <const char *>( memory->data() ),
|
||||
offsetof( InstancesInfo, checksum )
|
||||
);
|
||||
quint16 SingleApplicationPrivate::blockChecksum() {
|
||||
|
||||
return qChecksum(static_cast <const char *>(memory->data()), offsetof(InstancesInfo, checksum));
|
||||
|
||||
}
|
||||
|
||||
qint64 SingleApplicationPrivate::primaryPid()
|
||||
{
|
||||
qint64 pid;
|
||||
qint64 SingleApplicationPrivate::primaryPid() {
|
||||
|
||||
memory->lock();
|
||||
InstancesInfo* inst = static_cast<InstancesInfo*>( memory->data() );
|
||||
pid = inst->primaryPid;
|
||||
memory->unlock();
|
||||
qint64 pid;
|
||||
|
||||
memory->lock();
|
||||
InstancesInfo* inst = static_cast<InstancesInfo*>(memory->data());
|
||||
pid = inst->primaryPid;
|
||||
memory->unlock();
|
||||
|
||||
return pid;
|
||||
|
||||
return pid;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Executed when a connection has been made to the LocalServer
|
||||
*/
|
||||
void SingleApplicationPrivate::slotConnectionEstablished()
|
||||
{
|
||||
QLocalSocket *nextConnSocket = server->nextPendingConnection();
|
||||
connectionMap.insert(nextConnSocket, ConnectionInfo());
|
||||
void SingleApplicationPrivate::slotConnectionEstablished() {
|
||||
|
||||
QObject::connect(nextConnSocket, &QLocalSocket::aboutToClose,
|
||||
[nextConnSocket, this]() {
|
||||
auto &info = connectionMap[nextConnSocket];
|
||||
Q_EMIT this->slotClientConnectionClosed( nextConnSocket, info.instanceId );
|
||||
}
|
||||
);
|
||||
QLocalSocket *nextConnSocket = server->nextPendingConnection();
|
||||
connectionMap.insert(nextConnSocket, ConnectionInfo());
|
||||
|
||||
QObject::connect(nextConnSocket, &QLocalSocket::disconnected,
|
||||
[nextConnSocket, this](){
|
||||
connectionMap.remove(nextConnSocket);
|
||||
nextConnSocket->deleteLater();
|
||||
}
|
||||
);
|
||||
QObject::connect(nextConnSocket, &QLocalSocket::aboutToClose,
|
||||
[nextConnSocket, this]() {
|
||||
auto &info = connectionMap[nextConnSocket];
|
||||
Q_EMIT this->slotClientConnectionClosed(nextConnSocket, info.instanceId);
|
||||
}
|
||||
);
|
||||
|
||||
QObject::connect(nextConnSocket, &QLocalSocket::disconnected,
|
||||
[nextConnSocket, this](){
|
||||
connectionMap.remove(nextConnSocket);
|
||||
nextConnSocket->deleteLater();
|
||||
}
|
||||
);
|
||||
|
||||
QObject::connect(nextConnSocket, &QLocalSocket::readyRead,
|
||||
[nextConnSocket, this]() {
|
||||
auto &info = connectionMap[nextConnSocket];
|
||||
switch(info.stage) {
|
||||
case StageHeader:
|
||||
readInitMessageHeader(nextConnSocket);
|
||||
break;
|
||||
case StageBody:
|
||||
readInitMessageBody(nextConnSocket);
|
||||
break;
|
||||
case StageConnected:
|
||||
Q_EMIT this->slotDataAvailable(nextConnSocket, info.instanceId);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
};
|
||||
}
|
||||
);
|
||||
|
||||
QObject::connect(nextConnSocket, &QLocalSocket::readyRead,
|
||||
[nextConnSocket, this]() {
|
||||
auto &info = connectionMap[nextConnSocket];
|
||||
switch(info.stage) {
|
||||
case StageHeader:
|
||||
readInitMessageHeader(nextConnSocket);
|
||||
break;
|
||||
case StageBody:
|
||||
readInitMessageBody(nextConnSocket);
|
||||
break;
|
||||
case StageConnected:
|
||||
Q_EMIT this->slotDataAvailable( nextConnSocket, info.instanceId );
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
};
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
void SingleApplicationPrivate::readInitMessageHeader( QLocalSocket *sock )
|
||||
{
|
||||
if (!connectionMap.contains( sock )) {
|
||||
return;
|
||||
}
|
||||
void SingleApplicationPrivate::readInitMessageHeader(QLocalSocket *sock) {
|
||||
|
||||
if( sock->bytesAvailable() < ( qint64 )sizeof( quint64 ) ) {
|
||||
return;
|
||||
}
|
||||
if (!connectionMap.contains(sock)) {
|
||||
return;
|
||||
}
|
||||
|
||||
QDataStream headerStream( sock );
|
||||
if (sock->bytesAvailable() < (qint64) sizeof(quint64)) {
|
||||
return;
|
||||
}
|
||||
|
||||
QDataStream headerStream(sock);
|
||||
|
||||
#if (QT_VERSION >= QT_VERSION_CHECK(5, 6, 0))
|
||||
headerStream.setVersion( QDataStream::Qt_5_6 );
|
||||
headerStream.setVersion(QDataStream::Qt_5_6);
|
||||
#endif
|
||||
|
||||
// Read the header to know the message length
|
||||
quint64 msgLen = 0;
|
||||
headerStream >> msgLen;
|
||||
ConnectionInfo &info = connectionMap[sock];
|
||||
info.stage = StageBody;
|
||||
info.msgLen = msgLen;
|
||||
// Read the header to know the message length
|
||||
quint64 msgLen = 0;
|
||||
headerStream >> msgLen;
|
||||
ConnectionInfo &info = connectionMap[sock];
|
||||
info.stage = StageBody;
|
||||
info.msgLen = msgLen;
|
||||
|
||||
if (sock->bytesAvailable() >= (qint64) msgLen) {
|
||||
readInitMessageBody(sock);
|
||||
}
|
||||
|
||||
if ( sock->bytesAvailable() >= (qint64) msgLen ) {
|
||||
readInitMessageBody( sock );
|
||||
}
|
||||
}
|
||||
|
||||
void SingleApplicationPrivate::readInitMessageBody( QLocalSocket *sock )
|
||||
{
|
||||
Q_Q(SingleApplication);
|
||||
void SingleApplicationPrivate::readInitMessageBody(QLocalSocket *sock) {
|
||||
|
||||
if (!connectionMap.contains( sock )) {
|
||||
return;
|
||||
}
|
||||
Q_Q(SingleApplication);
|
||||
|
||||
ConnectionInfo &info = connectionMap[sock];
|
||||
if( sock->bytesAvailable() < ( qint64 )info.msgLen ) {
|
||||
return;
|
||||
}
|
||||
if (!connectionMap.contains(sock)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Read the message body
|
||||
QByteArray msgBytes = sock->read(info.msgLen);
|
||||
QDataStream readStream(msgBytes);
|
||||
ConnectionInfo &info = connectionMap[sock];
|
||||
if (sock->bytesAvailable() < (qint64)info.msgLen) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Read the message body
|
||||
QByteArray msgBytes = sock->read(info.msgLen);
|
||||
QDataStream readStream(msgBytes);
|
||||
|
||||
#if (QT_VERSION >= QT_VERSION_CHECK(5, 6, 0))
|
||||
readStream.setVersion( QDataStream::Qt_5_6 );
|
||||
readStream.setVersion(QDataStream::Qt_5_6);
|
||||
#endif
|
||||
|
||||
// server name
|
||||
QByteArray latin1Name;
|
||||
readStream >> latin1Name;
|
||||
// server name
|
||||
QByteArray latin1Name;
|
||||
readStream >> latin1Name;
|
||||
|
||||
// connection type
|
||||
ConnectionType connectionType = InvalidConnection;
|
||||
quint8 connTypeVal = InvalidConnection;
|
||||
readStream >> connTypeVal;
|
||||
connectionType = static_cast <ConnectionType>( connTypeVal );
|
||||
// connection type
|
||||
ConnectionType connectionType = InvalidConnection;
|
||||
quint8 connTypeVal = InvalidConnection;
|
||||
readStream >> connTypeVal;
|
||||
connectionType = static_cast <ConnectionType>(connTypeVal);
|
||||
|
||||
// instance id
|
||||
quint32 instanceId = 0;
|
||||
readStream >> instanceId;
|
||||
// instance id
|
||||
quint32 instanceId = 0;
|
||||
readStream >> instanceId;
|
||||
|
||||
// checksum
|
||||
quint16 msgChecksum = 0;
|
||||
readStream >> msgChecksum;
|
||||
// checksum
|
||||
quint16 msgChecksum = 0;
|
||||
readStream >> msgChecksum;
|
||||
|
||||
const quint16 actualChecksum = qChecksum( msgBytes.constData(), static_cast<quint32>( msgBytes.length() - sizeof( quint16 ) ) );
|
||||
const quint16 actualChecksum = qChecksum(msgBytes.constData(), static_cast<quint32>(msgBytes.length() - sizeof(quint16)));
|
||||
|
||||
bool isValid = readStream.status() == QDataStream::Ok &&
|
||||
QLatin1String(latin1Name) == blockServerName &&
|
||||
msgChecksum == actualChecksum;
|
||||
bool isValid = readStream.status() == QDataStream::Ok && QLatin1String(latin1Name) == blockServerName && msgChecksum == actualChecksum;
|
||||
|
||||
if( !isValid ) {
|
||||
sock->close();
|
||||
return;
|
||||
}
|
||||
if (!isValid) {
|
||||
sock->close();
|
||||
return;
|
||||
}
|
||||
|
||||
info.instanceId = instanceId;
|
||||
info.stage = StageConnected;
|
||||
info.instanceId = instanceId;
|
||||
info.stage = StageConnected;
|
||||
|
||||
if( connectionType == NewInstance ||
|
||||
( connectionType == SecondaryInstance &&
|
||||
options & SingleApplication::Mode::SecondaryNotification ) )
|
||||
{
|
||||
Q_EMIT q->instanceStarted();
|
||||
}
|
||||
if (connectionType == NewInstance || (connectionType == SecondaryInstance && options & SingleApplication::Mode::SecondaryNotification)) {
|
||||
Q_EMIT q->instanceStarted();
|
||||
}
|
||||
|
||||
if (sock->bytesAvailable() > 0) {
|
||||
Q_EMIT this->slotDataAvailable(sock, instanceId);
|
||||
}
|
||||
|
||||
if (sock->bytesAvailable() > 0) {
|
||||
Q_EMIT this->slotDataAvailable( sock, instanceId );
|
||||
}
|
||||
}
|
||||
|
||||
void SingleApplicationPrivate::slotDataAvailable( QLocalSocket *dataSocket, quint32 instanceId )
|
||||
{
|
||||
Q_Q(SingleApplication);
|
||||
Q_EMIT q->receivedMessage( instanceId, dataSocket->readAll() );
|
||||
void SingleApplicationPrivate::slotDataAvailable(QLocalSocket *dataSocket, quint32 instanceId) {
|
||||
|
||||
Q_Q(SingleApplication);
|
||||
Q_EMIT q->receivedMessage(instanceId, dataSocket->readAll());
|
||||
|
||||
}
|
||||
|
||||
void SingleApplicationPrivate::slotClientConnectionClosed( QLocalSocket *closedSocket, quint32 instanceId )
|
||||
{
|
||||
if( closedSocket->bytesAvailable() > 0 )
|
||||
Q_EMIT slotDataAvailable( closedSocket, instanceId );
|
||||
void SingleApplicationPrivate::slotClientConnectionClosed(QLocalSocket *closedSocket, quint32 instanceId) {
|
||||
|
||||
if (closedSocket->bytesAvailable() > 0)
|
||||
Q_EMIT slotDataAvailable(closedSocket, instanceId);
|
||||
|
||||
}
|
||||
|
||||
110
3rdparty/singleapplication/singleapplication_p.h
vendored
110
3rdparty/singleapplication/singleapplication_p.h
vendored
@@ -24,76 +24,80 @@
|
||||
// W A R N I N G !!!
|
||||
// -----------------
|
||||
//
|
||||
// This file is not part of the SingleApplication API. It is used purely as an
|
||||
// implementation detail. This header file may change from version to
|
||||
// version without notice, or may even be removed.
|
||||
// This is a modified version of SingleApplication,
|
||||
// The original version is at:
|
||||
//
|
||||
// https://github.com/itay-grudev/SingleApplication
|
||||
//
|
||||
//
|
||||
|
||||
#ifndef SINGLEAPPLICATION_P_H
|
||||
#define SINGLEAPPLICATION_P_H
|
||||
|
||||
#include <QtCore/QSharedMemory>
|
||||
#include <QtNetwork/QLocalServer>
|
||||
#include <QtNetwork/QLocalSocket>
|
||||
#include <QtGlobal>
|
||||
#include <QLocalSocket>
|
||||
#include <QLocalServer>
|
||||
#include <QSharedMemory>
|
||||
#include <QMap>
|
||||
|
||||
#include "singleapplication.h"
|
||||
|
||||
struct InstancesInfo {
|
||||
bool primary;
|
||||
quint32 secondary;
|
||||
qint64 primaryPid;
|
||||
quint16 checksum;
|
||||
bool primary;
|
||||
quint32 secondary;
|
||||
qint64 primaryPid;
|
||||
quint16 checksum;
|
||||
};
|
||||
|
||||
struct ConnectionInfo {
|
||||
explicit ConnectionInfo() :
|
||||
msgLen(0), instanceId(0), stage(0) {}
|
||||
qint64 msgLen;
|
||||
quint32 instanceId;
|
||||
quint8 stage;
|
||||
explicit ConnectionInfo() : msgLen(0), instanceId(0), stage(0) {}
|
||||
qint64 msgLen;
|
||||
quint32 instanceId;
|
||||
quint8 stage;
|
||||
};
|
||||
|
||||
class SingleApplicationPrivate : public QObject {
|
||||
Q_OBJECT
|
||||
public:
|
||||
enum ConnectionType : quint8 {
|
||||
InvalidConnection = 0,
|
||||
NewInstance = 1,
|
||||
SecondaryInstance = 2,
|
||||
Reconnect = 3
|
||||
};
|
||||
enum ConnectionStage : quint8 {
|
||||
StageHeader = 0,
|
||||
StageBody = 1,
|
||||
StageConnected = 2,
|
||||
};
|
||||
Q_DECLARE_PUBLIC(SingleApplication)
|
||||
Q_OBJECT
|
||||
public:
|
||||
enum ConnectionType : quint8 {
|
||||
InvalidConnection = 0,
|
||||
NewInstance = 1,
|
||||
SecondaryInstance = 2,
|
||||
Reconnect = 3
|
||||
};
|
||||
enum ConnectionStage : quint8 {
|
||||
StageHeader = 0,
|
||||
StageBody = 1,
|
||||
StageConnected = 2,
|
||||
};
|
||||
Q_DECLARE_PUBLIC(SingleApplication)
|
||||
|
||||
SingleApplicationPrivate( SingleApplication *q_ptr );
|
||||
~SingleApplicationPrivate();
|
||||
SingleApplicationPrivate( SingleApplication *q_ptr );
|
||||
~SingleApplicationPrivate();
|
||||
|
||||
void genBlockServerName();
|
||||
void initializeMemoryBlock();
|
||||
void startPrimary();
|
||||
void startSecondary();
|
||||
void connectToPrimary(int msecs, ConnectionType connectionType );
|
||||
quint16 blockChecksum();
|
||||
qint64 primaryPid();
|
||||
void readInitMessageHeader(QLocalSocket *socket);
|
||||
void readInitMessageBody(QLocalSocket *socket);
|
||||
void genBlockServerName();
|
||||
void initializeMemoryBlock();
|
||||
void startPrimary();
|
||||
void startSecondary();
|
||||
void connectToPrimary(int msecs, ConnectionType connectionType );
|
||||
quint16 blockChecksum();
|
||||
qint64 primaryPid();
|
||||
void readInitMessageHeader(QLocalSocket *socket);
|
||||
void readInitMessageBody(QLocalSocket *socket);
|
||||
|
||||
SingleApplication *q_ptr;
|
||||
QSharedMemory *memory;
|
||||
QLocalSocket *socket;
|
||||
QLocalServer *server;
|
||||
quint32 instanceNumber;
|
||||
QString blockServerName;
|
||||
SingleApplication::Options options;
|
||||
QMap<QLocalSocket*, ConnectionInfo> connectionMap;
|
||||
SingleApplication *q_ptr;
|
||||
QSharedMemory *memory;
|
||||
QLocalSocket *socket;
|
||||
QLocalServer *server;
|
||||
quint32 instanceNumber;
|
||||
QString blockServerName;
|
||||
SingleApplication::Options options;
|
||||
QMap<QLocalSocket*, ConnectionInfo> connectionMap;
|
||||
|
||||
public Q_SLOTS:
|
||||
void slotConnectionEstablished();
|
||||
void slotDataAvailable( QLocalSocket*, quint32 );
|
||||
void slotClientConnectionClosed( QLocalSocket*, quint32 );
|
||||
public slots:
|
||||
void slotConnectionEstablished();
|
||||
void slotDataAvailable(QLocalSocket*, quint32);
|
||||
void slotClientConnectionClosed(QLocalSocket*, quint32);
|
||||
};
|
||||
|
||||
#endif // SINGLEAPPLICATION_P_H
|
||||
#endif // SINGLEAPPLICATION_P_H
|
||||
|
||||
220
3rdparty/singleapplication/singlecoreapplication.cpp
vendored
220
3rdparty/singleapplication/singlecoreapplication.cpp
vendored
@@ -20,12 +20,24 @@
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
// THE SOFTWARE.
|
||||
|
||||
//
|
||||
// W A R N I N G !!!
|
||||
// -----------------
|
||||
//
|
||||
// This is a modified version of SingleApplication,
|
||||
// The original version is at:
|
||||
//
|
||||
// https://github.com/itay-grudev/SingleApplication
|
||||
//
|
||||
//
|
||||
|
||||
#include <QtGlobal>
|
||||
#include <QCoreApplication>
|
||||
#include <QtCore/QTime>
|
||||
#include <QtCore/QThread>
|
||||
#include <QtCore/QDateTime>
|
||||
#include <QtCore/QByteArray>
|
||||
#include <QtCore/QSharedMemory>
|
||||
#include <QThread>
|
||||
#include <QSharedMemory>
|
||||
#include <QByteArray>
|
||||
#include <QDateTime>
|
||||
#include <QTime>
|
||||
|
||||
#include "singlecoreapplication.h"
|
||||
#include "singlecoreapplication_p.h"
|
||||
@@ -37,139 +49,137 @@
|
||||
* @param argv
|
||||
* @param {bool} allowSecondaryInstances
|
||||
*/
|
||||
SingleCoreApplication::SingleCoreApplication( int &argc, char *argv[], bool allowSecondary, Options options, int timeout )
|
||||
: app_t( argc, argv ), d_ptr( new SingleCoreApplicationPrivate( this ) )
|
||||
{
|
||||
Q_D(SingleCoreApplication);
|
||||
SingleCoreApplication::SingleCoreApplication(int &argc, char *argv[], bool allowSecondary, Options options, int timeout)
|
||||
: app_t(argc, argv), d_ptr(new SingleCoreApplicationPrivate(this)) {
|
||||
|
||||
// Store the current mode of the program
|
||||
d->options = options;
|
||||
Q_D(SingleCoreApplication);
|
||||
|
||||
// Generating an application ID used for identifying the shared memory
|
||||
// block and QLocalServer
|
||||
d->genBlockServerName();
|
||||
// Store the current mode of the program
|
||||
d->options = options;
|
||||
|
||||
// Generating an application ID used for identifying the shared memory
|
||||
// block and QLocalServer
|
||||
d->genBlockServerName();
|
||||
|
||||
#ifdef Q_OS_UNIX
|
||||
// By explicitly attaching it and then deleting it we make sure that the
|
||||
// memory is deleted even after the process has crashed on Unix.
|
||||
d->memory = new QSharedMemory( d->blockServerName );
|
||||
d->memory->attach();
|
||||
delete d->memory;
|
||||
// By explicitly attaching it and then deleting it we make sure that the
|
||||
// memory is deleted even after the process has crashed on Unix.
|
||||
d->memory = new QSharedMemory(d->blockServerName);
|
||||
d->memory->attach();
|
||||
delete d->memory;
|
||||
#endif
|
||||
// Guarantee thread safe behaviour with a shared memory block.
|
||||
d->memory = new QSharedMemory( d->blockServerName );
|
||||
// Guarantee thread safe behaviour with a shared memory block.
|
||||
d->memory = new QSharedMemory(d->blockServerName);
|
||||
|
||||
// Create a shared memory block
|
||||
if( d->memory->create( sizeof( InstancesInfo ) ) ) {
|
||||
// Initialize the shared memory block
|
||||
d->memory->lock();
|
||||
d->initializeMemoryBlock();
|
||||
d->memory->unlock();
|
||||
} else {
|
||||
// Attempt to attach to the memory segment
|
||||
if( ! d->memory->attach() ) {
|
||||
qCritical() << "SingleCoreApplication: Unable to attach to shared memory block.";
|
||||
qCritical() << d->memory->errorString();
|
||||
delete d;
|
||||
::exit( EXIT_FAILURE );
|
||||
}
|
||||
// Create a shared memory block
|
||||
if (d->memory->create(sizeof(InstancesInfo))) {
|
||||
// Initialize the shared memory block
|
||||
d->memory->lock();
|
||||
d->initializeMemoryBlock();
|
||||
d->memory->unlock();
|
||||
}
|
||||
else {
|
||||
// Attempt to attach to the memory segment
|
||||
if (!d->memory->attach()) {
|
||||
qCritical() << "SingleCoreApplication: Unable to attach to shared memory block.";
|
||||
qCritical() << d->memory->errorString();
|
||||
delete d;
|
||||
::exit(EXIT_FAILURE);
|
||||
}
|
||||
}
|
||||
|
||||
InstancesInfo* inst = static_cast<InstancesInfo*>( d->memory->data() );
|
||||
QTime time;
|
||||
time.start();
|
||||
InstancesInfo* inst = static_cast<InstancesInfo*>(d->memory->data());
|
||||
QTime time;
|
||||
time.start();
|
||||
|
||||
// Make sure the shared memory block is initialised and in consistent state
|
||||
while( true ) {
|
||||
d->memory->lock();
|
||||
// Make sure the shared memory block is initialised and in consistent state
|
||||
while (true) {
|
||||
d->memory->lock();
|
||||
|
||||
if( d->blockChecksum() == inst->checksum ) break;
|
||||
if(d->blockChecksum() == inst->checksum) break;
|
||||
|
||||
if( time.elapsed() > 5000 ) {
|
||||
qWarning() << "SingleCoreApplication: Shared memory block has been in an inconsistent state from more than 5s. Assuming primary instance failure.";
|
||||
d->initializeMemoryBlock();
|
||||
}
|
||||
|
||||
d->memory->unlock();
|
||||
|
||||
// Random sleep here limits the probability of a collision between two racing apps
|
||||
qsrand( QDateTime::currentMSecsSinceEpoch() % std::numeric_limits<uint>::max() );
|
||||
QThread::sleep( 8 + static_cast <unsigned long>( static_cast <float>( qrand() ) / RAND_MAX * 10 ) );
|
||||
}
|
||||
|
||||
if( inst->primary == false) {
|
||||
d->startPrimary();
|
||||
d->memory->unlock();
|
||||
return;
|
||||
}
|
||||
|
||||
// Check if another instance can be started
|
||||
if( allowSecondary ) {
|
||||
inst->secondary += 1;
|
||||
inst->checksum = d->blockChecksum();
|
||||
d->instanceNumber = inst->secondary;
|
||||
d->startSecondary();
|
||||
if( d->options & Mode::SecondaryNotification ) {
|
||||
d->connectToPrimary( timeout, SingleCoreApplicationPrivate::SecondaryInstance );
|
||||
}
|
||||
d->memory->unlock();
|
||||
return;
|
||||
if (time.elapsed() > 5000) {
|
||||
qWarning() << "SingleCoreApplication: Shared memory block has been in an inconsistent state from more than 5s. Assuming primary instance failure.";
|
||||
d->initializeMemoryBlock();
|
||||
}
|
||||
|
||||
d->memory->unlock();
|
||||
|
||||
d->connectToPrimary( timeout, SingleCoreApplicationPrivate::NewInstance );
|
||||
// Random sleep here limits the probability of a collision between two racing apps
|
||||
qsrand(QDateTime::currentMSecsSinceEpoch() % std::numeric_limits<uint>::max());
|
||||
QThread::sleep(8 + static_cast <unsigned long>(static_cast <float>(qrand()) / RAND_MAX * 10));
|
||||
}
|
||||
|
||||
delete d;
|
||||
if (inst->primary == false) {
|
||||
d->startPrimary();
|
||||
d->memory->unlock();
|
||||
return;
|
||||
}
|
||||
|
||||
// Check if another instance can be started
|
||||
if (allowSecondary) {
|
||||
inst->secondary += 1;
|
||||
inst->checksum = d->blockChecksum();
|
||||
d->instanceNumber = inst->secondary;
|
||||
d->startSecondary();
|
||||
if(d->options & Mode::SecondaryNotification) {
|
||||
d->connectToPrimary(timeout, SingleCoreApplicationPrivate::SecondaryInstance);
|
||||
}
|
||||
d->memory->unlock();
|
||||
return;
|
||||
}
|
||||
|
||||
d->memory->unlock();
|
||||
|
||||
d->connectToPrimary(timeout, SingleCoreApplicationPrivate::NewInstance);
|
||||
|
||||
delete d;
|
||||
|
||||
::exit(EXIT_SUCCESS);
|
||||
|
||||
::exit( EXIT_SUCCESS );
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Destructor
|
||||
*/
|
||||
SingleCoreApplication::~SingleCoreApplication()
|
||||
{
|
||||
Q_D(SingleCoreApplication);
|
||||
delete d;
|
||||
SingleCoreApplication::~SingleCoreApplication() {
|
||||
Q_D(SingleCoreApplication);
|
||||
delete d;
|
||||
}
|
||||
|
||||
bool SingleCoreApplication::isPrimary()
|
||||
{
|
||||
Q_D(SingleCoreApplication);
|
||||
return d->server != nullptr;
|
||||
bool SingleCoreApplication::isPrimary() {
|
||||
Q_D(SingleCoreApplication);
|
||||
return d->server != nullptr;
|
||||
}
|
||||
|
||||
bool SingleCoreApplication::isSecondary()
|
||||
{
|
||||
Q_D(SingleCoreApplication);
|
||||
return d->server == nullptr;
|
||||
bool SingleCoreApplication::isSecondary() {
|
||||
Q_D(SingleCoreApplication);
|
||||
return d->server == nullptr;
|
||||
}
|
||||
|
||||
quint32 SingleCoreApplication::instanceId()
|
||||
{
|
||||
Q_D(SingleCoreApplication);
|
||||
return d->instanceNumber;
|
||||
quint32 SingleCoreApplication::instanceId() {
|
||||
Q_D(SingleCoreApplication);
|
||||
return d->instanceNumber;
|
||||
}
|
||||
|
||||
qint64 SingleCoreApplication::primaryPid()
|
||||
{
|
||||
Q_D(SingleCoreApplication);
|
||||
return d->primaryPid();
|
||||
qint64 SingleCoreApplication::primaryPid() {
|
||||
Q_D(SingleCoreApplication);
|
||||
return d->primaryPid();
|
||||
}
|
||||
|
||||
bool SingleCoreApplication::sendMessage( QByteArray message, int timeout )
|
||||
{
|
||||
Q_D(SingleCoreApplication);
|
||||
bool SingleCoreApplication::sendMessage(QByteArray message, int timeout) {
|
||||
|
||||
// Nobody to connect to
|
||||
if( isPrimary() ) return false;
|
||||
Q_D(SingleCoreApplication);
|
||||
|
||||
// Make sure the socket is connected
|
||||
d->connectToPrimary( timeout, SingleCoreApplicationPrivate::Reconnect );
|
||||
// Nobody to connect to
|
||||
if(isPrimary()) return false;
|
||||
|
||||
// Make sure the socket is connected
|
||||
d->connectToPrimary(timeout, SingleCoreApplicationPrivate::Reconnect);
|
||||
|
||||
d->socket->write(message);
|
||||
bool dataWritten = d->socket->waitForBytesWritten(timeout);
|
||||
d->socket->flush();
|
||||
return dataWritten;
|
||||
|
||||
d->socket->write( message );
|
||||
bool dataWritten = d->socket->flush();
|
||||
d->socket->waitForBytesWritten( timeout );
|
||||
return dataWritten;
|
||||
}
|
||||
|
||||
170
3rdparty/singleapplication/singlecoreapplication.h
vendored
170
3rdparty/singleapplication/singlecoreapplication.h
vendored
@@ -20,109 +20,119 @@
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
// THE SOFTWARE.
|
||||
|
||||
//
|
||||
// W A R N I N G !!!
|
||||
// -----------------
|
||||
//
|
||||
// This is a modified version of SingleApplication,
|
||||
// The original version is at:
|
||||
//
|
||||
// https://github.com/itay-grudev/SingleApplication
|
||||
//
|
||||
//
|
||||
|
||||
#ifndef SINGLECOREAPPLICATION_H
|
||||
#define SINGLECOREAPPLICATION_H
|
||||
|
||||
#include <QtCore/QtGlobal>
|
||||
#include <QtGlobal>
|
||||
#include <QCoreApplication>
|
||||
#include <QtNetwork/QLocalSocket>
|
||||
#include <QLocalSocket>
|
||||
|
||||
class SingleCoreApplicationPrivate;
|
||||
|
||||
/**
|
||||
* @brief The SingleCoreApplication class handles multipe instances of the same
|
||||
* @brief The SingleCoreApplication class handles multiple instances of the same
|
||||
* Application
|
||||
* @see QCoreApplication
|
||||
*/
|
||||
class SingleCoreApplication : public QCoreApplication
|
||||
{
|
||||
Q_OBJECT
|
||||
class SingleCoreApplication : public QCoreApplication {
|
||||
Q_OBJECT
|
||||
|
||||
typedef QCoreApplication app_t;
|
||||
typedef QCoreApplication app_t;
|
||||
|
||||
public:
|
||||
/**
|
||||
* @brief Mode of operation of SingleCoreApplication.
|
||||
* Whether the block should be user-wide or system-wide and whether the
|
||||
* primary instance should be notified when a secondary instance had been
|
||||
* started.
|
||||
* @note Operating system can restrict the shared memory blocks to the same
|
||||
* user, in which case the User/System modes will have no effect and the
|
||||
* block will be user wide.
|
||||
* @enum
|
||||
*/
|
||||
enum Mode {
|
||||
User = 1 << 0,
|
||||
System = 1 << 1,
|
||||
SecondaryNotification = 1 << 2,
|
||||
ExcludeAppVersion = 1 << 3,
|
||||
ExcludeAppPath = 1 << 4
|
||||
};
|
||||
Q_DECLARE_FLAGS(Options, Mode)
|
||||
/**
|
||||
* @brief Mode of operation of SingleCoreApplication.
|
||||
* Whether the block should be user-wide or system-wide and whether the
|
||||
* primary instance should be notified when a secondary instance had been
|
||||
* started.
|
||||
* @note Operating system can restrict the shared memory blocks to the same
|
||||
* user, in which case the User/System modes will have no effect and the
|
||||
* block will be user wide.
|
||||
* @enum
|
||||
*/
|
||||
enum Mode {
|
||||
User = 1 << 0,
|
||||
System = 1 << 1,
|
||||
SecondaryNotification = 1 << 2,
|
||||
ExcludeAppVersion = 1 << 3,
|
||||
ExcludeAppPath = 1 << 4
|
||||
};
|
||||
Q_DECLARE_FLAGS(Options, Mode)
|
||||
|
||||
/**
|
||||
* @brief Intitializes a SingleCoreApplication instance with argc command line
|
||||
* arguments in argv
|
||||
* @arg {int &} argc - Number of arguments in argv
|
||||
* @arg {const char *[]} argv - Supplied command line arguments
|
||||
* @arg {bool} allowSecondary - Whether to start the instance as secondary
|
||||
* if there is already a primary instance.
|
||||
* @arg {Mode} mode - Whether for the SingleCoreApplication block to be applied
|
||||
* User wide or System wide.
|
||||
* @arg {int} timeout - Timeout to wait in miliseconds.
|
||||
* @note argc and argv may be changed as Qt removes arguments that it
|
||||
* recognizes
|
||||
* @note Mode::SecondaryNotification only works if set on both the primary
|
||||
* instance and the secondary instance.
|
||||
* @note The timeout is just a hint for the maximum time of blocking
|
||||
* operations. It does not guarantee that the SingleCoreApplication
|
||||
* initialisation will be completed in given time, though is a good hint.
|
||||
* Usually 4*timeout would be the worst case (fail) scenario.
|
||||
* @see See the corresponding QAPPLICATION_CLASS constructor for reference
|
||||
*/
|
||||
explicit SingleCoreApplication( int &argc, char *argv[], bool allowSecondary = false, Options options = Mode::User, int timeout = 1000 );
|
||||
~SingleCoreApplication();
|
||||
/**
|
||||
* @brief Intitializes a SingleCoreApplication instance with argc command line
|
||||
* arguments in argv
|
||||
* @arg {int &} argc - Number of arguments in argv
|
||||
* @arg {const char *[]} argv - Supplied command line arguments
|
||||
* @arg {bool} allowSecondary - Whether to start the instance as secondary
|
||||
* if there is already a primary instance.
|
||||
* @arg {Mode} mode - Whether for the SingleCoreApplication block to be applied
|
||||
* User wide or System wide.
|
||||
* @arg {int} timeout - Timeout to wait in milliseconds.
|
||||
* @note argc and argv may be changed as Qt removes arguments that it
|
||||
* recognizes
|
||||
* @note Mode::SecondaryNotification only works if set on both the primary
|
||||
* instance and the secondary instance.
|
||||
* @note The timeout is just a hint for the maximum time of blocking
|
||||
* operations. It does not guarantee that the SingleCoreApplication
|
||||
* initialisation will be completed in given time, though is a good hint.
|
||||
* Usually 4*timeout would be the worst case (fail) scenario.
|
||||
* @see See the corresponding QAPPLICATION_CLASS constructor for reference
|
||||
*/
|
||||
explicit SingleCoreApplication( int &argc, char *argv[], bool allowSecondary = false, Options options = Mode::User, int timeout = 1000 );
|
||||
~SingleCoreApplication();
|
||||
|
||||
/**
|
||||
* @brief Returns if the instance is the primary instance
|
||||
* @returns {bool}
|
||||
*/
|
||||
bool isPrimary();
|
||||
/**
|
||||
* @brief Returns if the instance is the primary instance
|
||||
* @returns {bool}
|
||||
*/
|
||||
bool isPrimary();
|
||||
|
||||
/**
|
||||
* @brief Returns if the instance is a secondary instance
|
||||
* @returns {bool}
|
||||
*/
|
||||
bool isSecondary();
|
||||
/**
|
||||
* @brief Returns if the instance is a secondary instance
|
||||
* @returns {bool}
|
||||
*/
|
||||
bool isSecondary();
|
||||
|
||||
/**
|
||||
* @brief Returns a unique identifier for the current instance
|
||||
* @returns {qint32}
|
||||
*/
|
||||
quint32 instanceId();
|
||||
/**
|
||||
* @brief Returns a unique identifier for the current instance
|
||||
* @returns {qint32}
|
||||
*/
|
||||
quint32 instanceId();
|
||||
|
||||
/**
|
||||
* @brief Returns the process ID (PID) of the primary instance
|
||||
* @returns {qint64}
|
||||
*/
|
||||
qint64 primaryPid();
|
||||
/**
|
||||
* @brief Returns the process ID (PID) of the primary instance
|
||||
* @returns {qint64}
|
||||
*/
|
||||
qint64 primaryPid();
|
||||
|
||||
/**
|
||||
* @brief Sends a message to the primary instance. Returns true on success.
|
||||
* @param {int} timeout - Timeout for connecting
|
||||
* @returns {bool}
|
||||
* @note sendMessage() will return false if invoked from the primary
|
||||
* instance.
|
||||
*/
|
||||
bool sendMessage( QByteArray message, int timeout = 100 );
|
||||
/**
|
||||
* @brief Sends a message to the primary instance. Returns true on success.
|
||||
* @param {int} timeout - Timeout for connecting
|
||||
* @returns {bool}
|
||||
* @note sendMessage() will return false if invoked from the primary
|
||||
* instance.
|
||||
*/
|
||||
bool sendMessage( QByteArray message, int timeout = 1000 );
|
||||
|
||||
Q_SIGNALS:
|
||||
void instanceStarted();
|
||||
void receivedMessage( quint32 instanceId, QByteArray message );
|
||||
signals:
|
||||
void instanceStarted();
|
||||
void receivedMessage( quint32 instanceId, QByteArray message );
|
||||
|
||||
private:
|
||||
SingleCoreApplicationPrivate *d_ptr;
|
||||
Q_DECLARE_PRIVATE(SingleCoreApplication)
|
||||
SingleCoreApplicationPrivate *d_ptr;
|
||||
Q_DECLARE_PRIVATE(SingleCoreApplication)
|
||||
};
|
||||
|
||||
Q_DECLARE_OPERATORS_FOR_FLAGS(SingleCoreApplication::Options)
|
||||
|
||||
@@ -24,381 +24,386 @@
|
||||
// W A R N I N G !!!
|
||||
// -----------------
|
||||
//
|
||||
// This file is not part of the SingleCoreApplication API. It is used purely as an
|
||||
// implementation detail. This header file may change from version to
|
||||
// version without notice, or may even be removed.
|
||||
// This is a modified version of SingleApplication,
|
||||
// The original version is at:
|
||||
//
|
||||
// https://github.com/itay-grudev/SingleApplication
|
||||
//
|
||||
//
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <QtGlobal>
|
||||
|
||||
#include <cstdlib>
|
||||
#include <cstddef>
|
||||
|
||||
#include <QtCore/QDir>
|
||||
#include <QtCore/QProcess>
|
||||
#include <QtCore/QByteArray>
|
||||
#include <QtCore/QSemaphore>
|
||||
#include <QtCore/QDataStream>
|
||||
#include <QtCore/QStandardPaths>
|
||||
#include <QtCore/QCryptographicHash>
|
||||
#include <QtNetwork/QLocalServer>
|
||||
#include <QtNetwork/QLocalSocket>
|
||||
#ifdef Q_OS_UNIX
|
||||
# include <unistd.h>
|
||||
# include <sys/types.h>
|
||||
# include <pwd.h>
|
||||
#endif
|
||||
|
||||
#include <QDir>
|
||||
#include <QByteArray>
|
||||
#include <QDataStream>
|
||||
#include <QCryptographicHash>
|
||||
#include <QLocalServer>
|
||||
#include <QLocalSocket>
|
||||
|
||||
#include "singlecoreapplication.h"
|
||||
#include "singlecoreapplication_p.h"
|
||||
|
||||
#ifdef Q_OS_WIN
|
||||
#include <windows.h>
|
||||
#include <lmcons.h>
|
||||
# include <windows.h>
|
||||
# include <lmcons.h>
|
||||
#endif
|
||||
|
||||
SingleCoreApplicationPrivate::SingleCoreApplicationPrivate( SingleCoreApplication *q_ptr )
|
||||
: q_ptr( q_ptr )
|
||||
{
|
||||
server = nullptr;
|
||||
socket = nullptr;
|
||||
memory = nullptr;
|
||||
instanceNumber = -1;
|
||||
}
|
||||
SingleCoreApplicationPrivate::SingleCoreApplicationPrivate(SingleCoreApplication *q_ptr)
|
||||
: q_ptr(q_ptr),
|
||||
memory(nullptr),
|
||||
socket(nullptr),
|
||||
server(nullptr),
|
||||
instanceNumber(-1)
|
||||
{}
|
||||
|
||||
SingleCoreApplicationPrivate::~SingleCoreApplicationPrivate()
|
||||
{
|
||||
if( socket != nullptr ) {
|
||||
socket->close();
|
||||
delete socket;
|
||||
}
|
||||
SingleCoreApplicationPrivate::~SingleCoreApplicationPrivate() {
|
||||
|
||||
memory->lock();
|
||||
InstancesInfo* inst = static_cast<InstancesInfo*>(memory->data());
|
||||
if( server != nullptr ) {
|
||||
server->close();
|
||||
delete server;
|
||||
inst->primary = false;
|
||||
inst->primaryPid = -1;
|
||||
inst->checksum = blockChecksum();
|
||||
}
|
||||
memory->unlock();
|
||||
if (socket != nullptr) {
|
||||
socket->close();
|
||||
delete socket;
|
||||
}
|
||||
|
||||
delete memory;
|
||||
}
|
||||
|
||||
void SingleCoreApplicationPrivate::genBlockServerName()
|
||||
{
|
||||
QCryptographicHash appData( QCryptographicHash::Sha256 );
|
||||
appData.addData( "SingleApplication", 17 );
|
||||
appData.addData( SingleCoreApplication::app_t::applicationName().toUtf8() );
|
||||
appData.addData( SingleCoreApplication::app_t::organizationName().toUtf8() );
|
||||
appData.addData( SingleCoreApplication::app_t::organizationDomain().toUtf8() );
|
||||
|
||||
if( ! (options & SingleCoreApplication::Mode::ExcludeAppVersion) ) {
|
||||
appData.addData( SingleCoreApplication::app_t::applicationVersion().toUtf8() );
|
||||
}
|
||||
|
||||
if( ! (options & SingleCoreApplication::Mode::ExcludeAppPath) ) {
|
||||
#ifdef Q_OS_WIN
|
||||
appData.addData( SingleCoreApplication::app_t::applicationFilePath().toLower().toUtf8() );
|
||||
#else
|
||||
appData.addData( SingleCoreApplication::app_t::applicationFilePath().toUtf8() );
|
||||
#endif
|
||||
}
|
||||
|
||||
// User level block requires a user specific data in the hash
|
||||
if( options & SingleCoreApplication::Mode::User ) {
|
||||
#ifdef Q_OS_WIN
|
||||
wchar_t username [ UNLEN + 1 ];
|
||||
// Specifies size of the buffer on input
|
||||
DWORD usernameLength = UNLEN + 1;
|
||||
if( GetUserNameW( username, &usernameLength ) ) {
|
||||
appData.addData( QString::fromWCharArray(username).toUtf8() );
|
||||
} else {
|
||||
appData.addData( QStandardPaths::standardLocations( QStandardPaths::HomeLocation ).join("").toUtf8() );
|
||||
}
|
||||
#endif
|
||||
#ifdef Q_OS_UNIX
|
||||
QProcess process;
|
||||
process.start( "whoami" );
|
||||
if( process.waitForFinished( 100 ) &&
|
||||
process.exitCode() == QProcess::NormalExit) {
|
||||
appData.addData( process.readLine() );
|
||||
} else {
|
||||
appData.addData(
|
||||
QDir(
|
||||
QStandardPaths::standardLocations( QStandardPaths::HomeLocation ).first()
|
||||
).absolutePath().toUtf8()
|
||||
);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
// Replace the backslash in RFC 2045 Base64 [a-zA-Z0-9+/=] to comply with
|
||||
// server naming requirements.
|
||||
blockServerName = appData.result().toBase64().replace("/", "_");
|
||||
}
|
||||
|
||||
void SingleCoreApplicationPrivate::initializeMemoryBlock()
|
||||
{
|
||||
InstancesInfo* inst = static_cast<InstancesInfo*>( memory->data() );
|
||||
memory->lock();
|
||||
InstancesInfo* inst = static_cast<InstancesInfo*>(memory->data());
|
||||
if (server != nullptr) {
|
||||
server->close();
|
||||
delete server;
|
||||
inst->primary = false;
|
||||
inst->secondary = 0;
|
||||
inst->primaryPid = -1;
|
||||
inst->checksum = blockChecksum();
|
||||
}
|
||||
memory->unlock();
|
||||
|
||||
delete memory;
|
||||
|
||||
}
|
||||
|
||||
void SingleCoreApplicationPrivate::startPrimary()
|
||||
{
|
||||
Q_Q(SingleCoreApplication);
|
||||
void SingleCoreApplicationPrivate::genBlockServerName() {
|
||||
|
||||
// Successful creation means that no main process exists
|
||||
// So we start a QLocalServer to listen for connections
|
||||
QLocalServer::removeServer( blockServerName );
|
||||
server = new QLocalServer();
|
||||
QCryptographicHash appData(QCryptographicHash::Sha256);
|
||||
appData.addData("SingleApplication", 17);
|
||||
appData.addData(SingleCoreApplication::app_t::applicationName().toUtf8());
|
||||
appData.addData(SingleCoreApplication::app_t::organizationName().toUtf8());
|
||||
appData.addData(SingleCoreApplication::app_t::organizationDomain().toUtf8());
|
||||
|
||||
// Restrict access to the socket according to the
|
||||
// SingleCoreApplication::Mode::User flag on User level or no restrictions
|
||||
if( options & SingleCoreApplication::Mode::User ) {
|
||||
server->setSocketOptions( QLocalServer::UserAccessOption );
|
||||
} else {
|
||||
server->setSocketOptions( QLocalServer::WorldAccessOption );
|
||||
if (!(options & SingleCoreApplication::Mode::ExcludeAppVersion)) {
|
||||
appData.addData(SingleCoreApplication::app_t::applicationVersion().toUtf8());
|
||||
}
|
||||
|
||||
if (!(options & SingleCoreApplication::Mode::ExcludeAppPath)) {
|
||||
#ifdef Q_OS_WIN
|
||||
appData.addData(SingleCoreApplication::app_t::applicationFilePath().toLower().toUtf8());
|
||||
#else
|
||||
appData.addData(SingleCoreApplication::app_t::applicationFilePath().toUtf8());
|
||||
#endif
|
||||
}
|
||||
|
||||
// User level block requires a user specific data in the hash
|
||||
if (options & SingleCoreApplication::Mode::User) {
|
||||
#ifdef Q_OS_UNIX
|
||||
QByteArray username;
|
||||
#if defined(HAVE_GETEUID) && defined(HAVE_GETPWUID)
|
||||
uid_t uid = geteuid();
|
||||
if (uid != -1) {
|
||||
struct passwd *pw = getpwuid(uid);
|
||||
if (pw) {
|
||||
username = pw->pw_name;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
if (username.isEmpty()) username = qgetenv("USER");
|
||||
appData.addData(username);
|
||||
#endif
|
||||
#ifdef Q_OS_WIN
|
||||
wchar_t username [ UNLEN + 1 ];
|
||||
// Specifies size of the buffer on input
|
||||
DWORD usernameLength = UNLEN + 1;
|
||||
if (GetUserNameW(username, &usernameLength)) {
|
||||
appData.addData(QString::fromWCharArray(username).toUtf8());
|
||||
}
|
||||
else {
|
||||
appData.addData(qgetenv("USERNAME"));
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
server->listen( blockServerName );
|
||||
QObject::connect(
|
||||
server,
|
||||
&QLocalServer::newConnection,
|
||||
this,
|
||||
&SingleCoreApplicationPrivate::slotConnectionEstablished
|
||||
);
|
||||
// Replace the backslash in RFC 2045 Base64 [a-zA-Z0-9+/=] to comply with server naming requirements.
|
||||
blockServerName = appData.result().toBase64().replace("/", "_");
|
||||
|
||||
// Reset the number of connections
|
||||
InstancesInfo* inst = static_cast <InstancesInfo*>( memory->data() );
|
||||
|
||||
inst->primary = true;
|
||||
inst->primaryPid = q->applicationPid();
|
||||
inst->checksum = blockChecksum();
|
||||
|
||||
instanceNumber = 0;
|
||||
}
|
||||
|
||||
void SingleCoreApplicationPrivate::startSecondary()
|
||||
{
|
||||
void SingleCoreApplicationPrivate::initializeMemoryBlock() {
|
||||
|
||||
InstancesInfo* inst = static_cast<InstancesInfo*>(memory->data());
|
||||
inst->primary = false;
|
||||
inst->secondary = 0;
|
||||
inst->primaryPid = -1;
|
||||
inst->checksum = blockChecksum();
|
||||
|
||||
}
|
||||
|
||||
void SingleCoreApplicationPrivate::connectToPrimary( int msecs, ConnectionType connectionType )
|
||||
{
|
||||
// Connect to the Local Server of the Primary Instance if not already
|
||||
// connected.
|
||||
if( socket == nullptr ) {
|
||||
socket = new QLocalSocket();
|
||||
}
|
||||
void SingleCoreApplicationPrivate::startPrimary() {
|
||||
|
||||
// If already connected - we are done;
|
||||
if( socket->state() == QLocalSocket::ConnectedState )
|
||||
return;
|
||||
Q_Q(SingleCoreApplication);
|
||||
|
||||
// If not connect
|
||||
if( socket->state() == QLocalSocket::UnconnectedState ||
|
||||
socket->state() == QLocalSocket::ClosingState ) {
|
||||
socket->connectToServer( blockServerName );
|
||||
}
|
||||
// Successful creation means that no main process exists
|
||||
// So we start a QLocalServer to listen for connections
|
||||
QLocalServer::removeServer(blockServerName);
|
||||
server = new QLocalServer();
|
||||
|
||||
// Wait for being connected
|
||||
if( socket->state() == QLocalSocket::ConnectingState ) {
|
||||
socket->waitForConnected( msecs );
|
||||
}
|
||||
// Restrict access to the socket according to the
|
||||
// SingleCoreApplication::Mode::User flag on User level or no restrictions
|
||||
if (options & SingleCoreApplication::Mode::User) {
|
||||
server->setSocketOptions(QLocalServer::UserAccessOption);
|
||||
}
|
||||
else {
|
||||
server->setSocketOptions(QLocalServer::WorldAccessOption);
|
||||
}
|
||||
|
||||
// Initialisation message according to the SingleCoreApplication protocol
|
||||
if( socket->state() == QLocalSocket::ConnectedState ) {
|
||||
// Notify the parent that a new instance had been started;
|
||||
QByteArray initMsg;
|
||||
QDataStream writeStream(&initMsg, QIODevice::WriteOnly);
|
||||
server->listen(blockServerName);
|
||||
QObject::connect(server, &QLocalServer::newConnection, this, &SingleCoreApplicationPrivate::slotConnectionEstablished);
|
||||
|
||||
// Reset the number of connections
|
||||
InstancesInfo* inst = static_cast <InstancesInfo*>(memory->data());
|
||||
|
||||
inst->primary = true;
|
||||
inst->primaryPid = q->applicationPid();
|
||||
inst->checksum = blockChecksum();
|
||||
|
||||
instanceNumber = 0;
|
||||
|
||||
}
|
||||
|
||||
void SingleCoreApplicationPrivate::startSecondary() {}
|
||||
|
||||
void SingleCoreApplicationPrivate::connectToPrimary(int msecs, ConnectionType connectionType) {
|
||||
|
||||
// Connect to the Local Server of the Primary Instance if not already connected.
|
||||
if (socket == nullptr) {
|
||||
socket = new QLocalSocket();
|
||||
}
|
||||
|
||||
// If already connected - we are done;
|
||||
if (socket->state() == QLocalSocket::ConnectedState)
|
||||
return;
|
||||
|
||||
// If not connect
|
||||
if (socket->state() == QLocalSocket::UnconnectedState ||
|
||||
socket->state() == QLocalSocket::ClosingState) {
|
||||
socket->connectToServer(blockServerName);
|
||||
}
|
||||
|
||||
// Wait for being connected
|
||||
if (socket->state() == QLocalSocket::ConnectingState) {
|
||||
socket->waitForConnected(msecs);
|
||||
}
|
||||
|
||||
// Initialisation message according to the SingleCoreApplication protocol
|
||||
if (socket->state() == QLocalSocket::ConnectedState) {
|
||||
// Notify the parent that a new instance had been started;
|
||||
QByteArray initMsg;
|
||||
QDataStream writeStream(&initMsg, QIODevice::WriteOnly);
|
||||
|
||||
#if (QT_VERSION >= QT_VERSION_CHECK(5, 6, 0))
|
||||
writeStream.setVersion(QDataStream::Qt_5_6);
|
||||
writeStream.setVersion(QDataStream::Qt_5_6);
|
||||
#endif
|
||||
|
||||
writeStream << blockServerName.toLatin1();
|
||||
writeStream << static_cast<quint8>(connectionType);
|
||||
writeStream << instanceNumber;
|
||||
quint16 checksum = qChecksum(initMsg.constData(), static_cast<quint32>(initMsg.length()));
|
||||
writeStream << checksum;
|
||||
writeStream << blockServerName.toLatin1();
|
||||
writeStream << static_cast<quint8>(connectionType);
|
||||
writeStream << instanceNumber;
|
||||
quint16 checksum = qChecksum(initMsg.constData(), static_cast<quint32>(initMsg.length()));
|
||||
writeStream << checksum;
|
||||
|
||||
// The header indicates the message length that follows
|
||||
QByteArray header;
|
||||
QDataStream headerStream(&header, QIODevice::WriteOnly);
|
||||
// The header indicates the message length that follows
|
||||
QByteArray header;
|
||||
QDataStream headerStream(&header, QIODevice::WriteOnly);
|
||||
|
||||
#if (QT_VERSION >= QT_VERSION_CHECK(5, 6, 0))
|
||||
headerStream.setVersion(QDataStream::Qt_5_6);
|
||||
headerStream.setVersion(QDataStream::Qt_5_6);
|
||||
#endif
|
||||
headerStream << static_cast <quint64>( initMsg.length() );
|
||||
headerStream << static_cast <quint64>(initMsg.length());
|
||||
|
||||
socket->write(header);
|
||||
socket->write(initMsg);
|
||||
socket->flush();
|
||||
socket->waitForBytesWritten(msecs);
|
||||
}
|
||||
|
||||
socket->write( header );
|
||||
socket->write( initMsg );
|
||||
socket->flush();
|
||||
socket->waitForBytesWritten( msecs );
|
||||
}
|
||||
}
|
||||
|
||||
quint16 SingleCoreApplicationPrivate::blockChecksum()
|
||||
{
|
||||
return qChecksum(
|
||||
static_cast <const char *>( memory->data() ),
|
||||
offsetof( InstancesInfo, checksum )
|
||||
);
|
||||
quint16 SingleCoreApplicationPrivate::blockChecksum() {
|
||||
|
||||
return qChecksum(static_cast <const char*> (memory->data()), offsetof(InstancesInfo, checksum));
|
||||
|
||||
}
|
||||
|
||||
qint64 SingleCoreApplicationPrivate::primaryPid()
|
||||
{
|
||||
qint64 pid;
|
||||
qint64 SingleCoreApplicationPrivate::primaryPid() {
|
||||
|
||||
memory->lock();
|
||||
InstancesInfo* inst = static_cast<InstancesInfo*>( memory->data() );
|
||||
pid = inst->primaryPid;
|
||||
memory->unlock();
|
||||
qint64 pid;
|
||||
|
||||
memory->lock();
|
||||
InstancesInfo* inst = static_cast<InstancesInfo*>(memory->data());
|
||||
pid = inst->primaryPid;
|
||||
memory->unlock();
|
||||
|
||||
return pid;
|
||||
|
||||
return pid;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Executed when a connection has been made to the LocalServer
|
||||
*/
|
||||
void SingleCoreApplicationPrivate::slotConnectionEstablished()
|
||||
{
|
||||
QLocalSocket *nextConnSocket = server->nextPendingConnection();
|
||||
connectionMap.insert(nextConnSocket, ConnectionInfo());
|
||||
void SingleCoreApplicationPrivate::slotConnectionEstablished() {
|
||||
|
||||
QObject::connect(nextConnSocket, &QLocalSocket::aboutToClose,
|
||||
[nextConnSocket, this]() {
|
||||
auto &info = connectionMap[nextConnSocket];
|
||||
Q_EMIT this->slotClientConnectionClosed( nextConnSocket, info.instanceId );
|
||||
}
|
||||
);
|
||||
QLocalSocket *nextConnSocket = server->nextPendingConnection();
|
||||
connectionMap.insert(nextConnSocket, ConnectionInfo());
|
||||
|
||||
QObject::connect(nextConnSocket, &QLocalSocket::disconnected,
|
||||
[nextConnSocket, this](){
|
||||
connectionMap.remove(nextConnSocket);
|
||||
nextConnSocket->deleteLater();
|
||||
}
|
||||
);
|
||||
QObject::connect(nextConnSocket, &QLocalSocket::aboutToClose,
|
||||
[nextConnSocket, this]() {
|
||||
auto &info = connectionMap[nextConnSocket];
|
||||
Q_EMIT this->slotClientConnectionClosed(nextConnSocket, info.instanceId);
|
||||
}
|
||||
);
|
||||
|
||||
QObject::connect(nextConnSocket, &QLocalSocket::disconnected,
|
||||
[nextConnSocket, this](){
|
||||
connectionMap.remove(nextConnSocket);
|
||||
nextConnSocket->deleteLater();
|
||||
}
|
||||
);
|
||||
|
||||
QObject::connect(nextConnSocket, &QLocalSocket::readyRead,
|
||||
[nextConnSocket, this]() {
|
||||
auto &info = connectionMap[nextConnSocket];
|
||||
switch(info.stage) {
|
||||
case StageHeader:
|
||||
readInitMessageHeader(nextConnSocket);
|
||||
break;
|
||||
case StageBody:
|
||||
readInitMessageBody(nextConnSocket);
|
||||
break;
|
||||
case StageConnected:
|
||||
Q_EMIT this->slotDataAvailable(nextConnSocket, info.instanceId);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
};
|
||||
}
|
||||
);
|
||||
|
||||
QObject::connect(nextConnSocket, &QLocalSocket::readyRead,
|
||||
[nextConnSocket, this]() {
|
||||
auto &info = connectionMap[nextConnSocket];
|
||||
switch(info.stage) {
|
||||
case StageHeader:
|
||||
readInitMessageHeader(nextConnSocket);
|
||||
break;
|
||||
case StageBody:
|
||||
readInitMessageBody(nextConnSocket);
|
||||
break;
|
||||
case StageConnected:
|
||||
Q_EMIT this->slotDataAvailable( nextConnSocket, info.instanceId );
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
};
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
void SingleCoreApplicationPrivate::readInitMessageHeader( QLocalSocket *sock )
|
||||
{
|
||||
if (!connectionMap.contains( sock )) {
|
||||
return;
|
||||
}
|
||||
void SingleCoreApplicationPrivate::readInitMessageHeader(QLocalSocket *sock) {
|
||||
|
||||
if( sock->bytesAvailable() < ( qint64 )sizeof( quint64 ) ) {
|
||||
return;
|
||||
}
|
||||
if (!connectionMap.contains(sock)) {
|
||||
return;
|
||||
}
|
||||
|
||||
QDataStream headerStream( sock );
|
||||
if (sock->bytesAvailable() < (qint64)sizeof(quint64)) {
|
||||
return;
|
||||
}
|
||||
|
||||
QDataStream headerStream(sock);
|
||||
|
||||
#if (QT_VERSION >= QT_VERSION_CHECK(5, 6, 0))
|
||||
headerStream.setVersion( QDataStream::Qt_5_6 );
|
||||
headerStream.setVersion(QDataStream::Qt_5_6);
|
||||
#endif
|
||||
|
||||
// Read the header to know the message length
|
||||
quint64 msgLen = 0;
|
||||
headerStream >> msgLen;
|
||||
ConnectionInfo &info = connectionMap[sock];
|
||||
info.stage = StageBody;
|
||||
info.msgLen = msgLen;
|
||||
// Read the header to know the message length
|
||||
quint64 msgLen = 0;
|
||||
headerStream >> msgLen;
|
||||
ConnectionInfo &info = connectionMap[sock];
|
||||
info.stage = StageBody;
|
||||
info.msgLen = msgLen;
|
||||
|
||||
if (sock->bytesAvailable() >= (qint64) msgLen) {
|
||||
readInitMessageBody(sock);
|
||||
}
|
||||
|
||||
if ( sock->bytesAvailable() >= (qint64) msgLen ) {
|
||||
readInitMessageBody( sock );
|
||||
}
|
||||
}
|
||||
|
||||
void SingleCoreApplicationPrivate::readInitMessageBody( QLocalSocket *sock )
|
||||
{
|
||||
Q_Q(SingleCoreApplication);
|
||||
void SingleCoreApplicationPrivate::readInitMessageBody(QLocalSocket *sock) {
|
||||
|
||||
if (!connectionMap.contains( sock )) {
|
||||
return;
|
||||
}
|
||||
Q_Q(SingleCoreApplication);
|
||||
|
||||
ConnectionInfo &info = connectionMap[sock];
|
||||
if( sock->bytesAvailable() < ( qint64 )info.msgLen ) {
|
||||
return;
|
||||
}
|
||||
if (!connectionMap.contains(sock)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Read the message body
|
||||
QByteArray msgBytes = sock->read(info.msgLen);
|
||||
QDataStream readStream(msgBytes);
|
||||
ConnectionInfo &info = connectionMap[sock];
|
||||
if (sock->bytesAvailable() < (qint64)info.msgLen) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Read the message body
|
||||
QByteArray msgBytes = sock->read(info.msgLen);
|
||||
QDataStream readStream(msgBytes);
|
||||
|
||||
#if (QT_VERSION >= QT_VERSION_CHECK(5, 6, 0))
|
||||
readStream.setVersion( QDataStream::Qt_5_6 );
|
||||
readStream.setVersion(QDataStream::Qt_5_6);
|
||||
#endif
|
||||
|
||||
// server name
|
||||
QByteArray latin1Name;
|
||||
readStream >> latin1Name;
|
||||
// server name
|
||||
QByteArray latin1Name;
|
||||
readStream >> latin1Name;
|
||||
|
||||
// connection type
|
||||
ConnectionType connectionType = InvalidConnection;
|
||||
quint8 connTypeVal = InvalidConnection;
|
||||
readStream >> connTypeVal;
|
||||
connectionType = static_cast <ConnectionType>( connTypeVal );
|
||||
// connection type
|
||||
ConnectionType connectionType = InvalidConnection;
|
||||
quint8 connTypeVal = InvalidConnection;
|
||||
readStream >> connTypeVal;
|
||||
connectionType = static_cast <ConnectionType>(connTypeVal);
|
||||
|
||||
// instance id
|
||||
quint32 instanceId = 0;
|
||||
readStream >> instanceId;
|
||||
// instance id
|
||||
quint32 instanceId = 0;
|
||||
readStream >> instanceId;
|
||||
|
||||
// checksum
|
||||
quint16 msgChecksum = 0;
|
||||
readStream >> msgChecksum;
|
||||
// checksum
|
||||
quint16 msgChecksum = 0;
|
||||
readStream >> msgChecksum;
|
||||
|
||||
const quint16 actualChecksum = qChecksum( msgBytes.constData(), static_cast<quint32>( msgBytes.length() - sizeof( quint16 ) ) );
|
||||
const quint16 actualChecksum = qChecksum(msgBytes.constData(), static_cast<quint32>(msgBytes.length() - sizeof(quint16)));
|
||||
|
||||
bool isValid = readStream.status() == QDataStream::Ok &&
|
||||
QLatin1String(latin1Name) == blockServerName &&
|
||||
msgChecksum == actualChecksum;
|
||||
bool isValid = readStream.status() == QDataStream::Ok && QLatin1String(latin1Name) == blockServerName && msgChecksum == actualChecksum;
|
||||
|
||||
if( !isValid ) {
|
||||
sock->close();
|
||||
return;
|
||||
}
|
||||
if (!isValid) {
|
||||
sock->close();
|
||||
return;
|
||||
}
|
||||
|
||||
info.instanceId = instanceId;
|
||||
info.stage = StageConnected;
|
||||
info.instanceId = instanceId;
|
||||
info.stage = StageConnected;
|
||||
|
||||
if( connectionType == NewInstance ||
|
||||
( connectionType == SecondaryInstance &&
|
||||
options & SingleCoreApplication::Mode::SecondaryNotification ) )
|
||||
{
|
||||
Q_EMIT q->instanceStarted();
|
||||
}
|
||||
if (connectionType == NewInstance || (connectionType == SecondaryInstance && options & SingleCoreApplication::Mode::SecondaryNotification)) {
|
||||
Q_EMIT q->instanceStarted();
|
||||
}
|
||||
|
||||
if (sock->bytesAvailable() > 0) {
|
||||
Q_EMIT this->slotDataAvailable(sock, instanceId);
|
||||
}
|
||||
|
||||
if (sock->bytesAvailable() > 0) {
|
||||
Q_EMIT this->slotDataAvailable( sock, instanceId );
|
||||
}
|
||||
}
|
||||
|
||||
void SingleCoreApplicationPrivate::slotDataAvailable( QLocalSocket *dataSocket, quint32 instanceId )
|
||||
{
|
||||
Q_Q(SingleCoreApplication);
|
||||
Q_EMIT q->receivedMessage( instanceId, dataSocket->readAll() );
|
||||
void SingleCoreApplicationPrivate::slotDataAvailable(QLocalSocket *dataSocket, quint32 instanceId) {
|
||||
|
||||
Q_Q(SingleCoreApplication);
|
||||
Q_EMIT q->receivedMessage(instanceId, dataSocket->readAll());
|
||||
|
||||
}
|
||||
|
||||
void SingleCoreApplicationPrivate::slotClientConnectionClosed( QLocalSocket *closedSocket, quint32 instanceId )
|
||||
{
|
||||
if( closedSocket->bytesAvailable() > 0 )
|
||||
Q_EMIT slotDataAvailable( closedSocket, instanceId );
|
||||
void SingleCoreApplicationPrivate::slotClientConnectionClosed(QLocalSocket *closedSocket, quint32 instanceId) {
|
||||
|
||||
if (closedSocket->bytesAvailable() > 0)
|
||||
Q_EMIT slotDataAvailable(closedSocket, instanceId);
|
||||
|
||||
}
|
||||
|
||||
110
3rdparty/singleapplication/singlecoreapplication_p.h
vendored
110
3rdparty/singleapplication/singlecoreapplication_p.h
vendored
@@ -24,76 +24,80 @@
|
||||
// W A R N I N G !!!
|
||||
// -----------------
|
||||
//
|
||||
// This file is not part of the SingleCoreApplication API. It is used purely as an
|
||||
// implementation detail. This header file may change from version to
|
||||
// version without notice, or may even be removed.
|
||||
// This is a modified version of SingleApplication,
|
||||
// The original version is at:
|
||||
//
|
||||
// https://github.com/itay-grudev/SingleApplication
|
||||
//
|
||||
//
|
||||
|
||||
#ifndef SINGLECOREAPPLICATION_P_H
|
||||
#define SINGLECOREAPPLICATION_P_H
|
||||
|
||||
#include <QtCore/QSharedMemory>
|
||||
#include <QtNetwork/QLocalServer>
|
||||
#include <QtNetwork/QLocalSocket>
|
||||
#include <QtGlobal>
|
||||
#include <QLocalSocket>
|
||||
#include <QLocalServer>
|
||||
#include <QSharedMemory>
|
||||
#include <QMap>
|
||||
|
||||
#include "singlecoreapplication.h"
|
||||
|
||||
struct InstancesInfo {
|
||||
bool primary;
|
||||
quint32 secondary;
|
||||
qint64 primaryPid;
|
||||
quint16 checksum;
|
||||
bool primary;
|
||||
quint32 secondary;
|
||||
qint64 primaryPid;
|
||||
quint16 checksum;
|
||||
};
|
||||
|
||||
struct ConnectionInfo {
|
||||
explicit ConnectionInfo() :
|
||||
msgLen(0), instanceId(0), stage(0) {}
|
||||
qint64 msgLen;
|
||||
quint32 instanceId;
|
||||
quint8 stage;
|
||||
explicit ConnectionInfo() : msgLen(0), instanceId(0), stage(0) {}
|
||||
qint64 msgLen;
|
||||
quint32 instanceId;
|
||||
quint8 stage;
|
||||
};
|
||||
|
||||
class SingleCoreApplicationPrivate : public QObject {
|
||||
Q_OBJECT
|
||||
public:
|
||||
enum ConnectionType : quint8 {
|
||||
InvalidConnection = 0,
|
||||
NewInstance = 1,
|
||||
SecondaryInstance = 2,
|
||||
Reconnect = 3
|
||||
};
|
||||
enum ConnectionStage : quint8 {
|
||||
StageHeader = 0,
|
||||
StageBody = 1,
|
||||
StageConnected = 2,
|
||||
};
|
||||
Q_DECLARE_PUBLIC(SingleCoreApplication)
|
||||
Q_OBJECT
|
||||
public:
|
||||
enum ConnectionType : quint8 {
|
||||
InvalidConnection = 0,
|
||||
NewInstance = 1,
|
||||
SecondaryInstance = 2,
|
||||
Reconnect = 3
|
||||
};
|
||||
enum ConnectionStage : quint8 {
|
||||
StageHeader = 0,
|
||||
StageBody = 1,
|
||||
StageConnected = 2,
|
||||
};
|
||||
Q_DECLARE_PUBLIC(SingleCoreApplication)
|
||||
|
||||
SingleCoreApplicationPrivate( SingleCoreApplication *q_ptr );
|
||||
~SingleCoreApplicationPrivate();
|
||||
SingleCoreApplicationPrivate( SingleCoreApplication *q_ptr );
|
||||
~SingleCoreApplicationPrivate();
|
||||
|
||||
void genBlockServerName();
|
||||
void initializeMemoryBlock();
|
||||
void startPrimary();
|
||||
void startSecondary();
|
||||
void connectToPrimary(int msecs, ConnectionType connectionType );
|
||||
quint16 blockChecksum();
|
||||
qint64 primaryPid();
|
||||
void readInitMessageHeader(QLocalSocket *socket);
|
||||
void readInitMessageBody(QLocalSocket *socket);
|
||||
void genBlockServerName();
|
||||
void initializeMemoryBlock();
|
||||
void startPrimary();
|
||||
void startSecondary();
|
||||
void connectToPrimary(int msecs, ConnectionType connectionType );
|
||||
quint16 blockChecksum();
|
||||
qint64 primaryPid();
|
||||
void readInitMessageHeader(QLocalSocket *socket);
|
||||
void readInitMessageBody(QLocalSocket *socket);
|
||||
|
||||
SingleCoreApplication *q_ptr;
|
||||
QSharedMemory *memory;
|
||||
QLocalSocket *socket;
|
||||
QLocalServer *server;
|
||||
quint32 instanceNumber;
|
||||
QString blockServerName;
|
||||
SingleCoreApplication::Options options;
|
||||
QMap<QLocalSocket*, ConnectionInfo> connectionMap;
|
||||
SingleCoreApplication *q_ptr;
|
||||
QSharedMemory *memory;
|
||||
QLocalSocket *socket;
|
||||
QLocalServer *server;
|
||||
quint32 instanceNumber;
|
||||
QString blockServerName;
|
||||
SingleCoreApplication::Options options;
|
||||
QMap<QLocalSocket*, ConnectionInfo> connectionMap;
|
||||
|
||||
public Q_SLOTS:
|
||||
void slotConnectionEstablished();
|
||||
void slotDataAvailable( QLocalSocket*, quint32 );
|
||||
void slotClientConnectionClosed( QLocalSocket*, quint32 );
|
||||
public slots:
|
||||
void slotConnectionEstablished();
|
||||
void slotDataAvailable(QLocalSocket*, quint32);
|
||||
void slotClientConnectionClosed(QLocalSocket*, quint32);
|
||||
};
|
||||
|
||||
#endif // SINGLECOREAPPLICATION_P_H
|
||||
#endif // SINGLECOREAPPLICATION_P_H
|
||||
|
||||
10
3rdparty/taglib/CMakeLists.txt
vendored
10
3rdparty/taglib/CMakeLists.txt
vendored
@@ -1,6 +1,8 @@
|
||||
cmake_minimum_required(VERSION 2.8.11)
|
||||
|
||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall")
|
||||
set(CMAKE_CXX_STANDARD 11)
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-delete-non-virtual-dtor")
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} --std=c++11 -U__STRICT_ANSI__ -fpermissive -Wall -Woverloaded-virtual -Wno-sign-compare -Wno-delete-non-virtual-dtor")
|
||||
|
||||
set(TAGLIB_SOVERSION_CURRENT 17)
|
||||
set(TAGLIB_SOVERSION_REVISION 0)
|
||||
@@ -19,8 +21,13 @@ else()
|
||||
add_definitions(-DSYSTEM_BYTEORDER=2)
|
||||
endif()
|
||||
|
||||
include(ConfigureChecks.cmake)
|
||||
configure_file(config.h.cmake "${CMAKE_CURRENT_BINARY_DIR}/config.h")
|
||||
configure_file(taglib_config.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/taglib_config.h)
|
||||
|
||||
add_definitions(-DHAVE_CONFIG_H)
|
||||
add_definitions(-DTAGLIB_STATIC)
|
||||
|
||||
set(CMAKE_INCLUDE_CURRENT_DIR ON)
|
||||
include_directories(
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/toolkit
|
||||
@@ -86,6 +93,7 @@ set(tag_HDRS
|
||||
mpeg/xingheader.h
|
||||
mpeg/id3v1/id3v1tag.h
|
||||
mpeg/id3v1/id3v1genres.h
|
||||
mpeg/id3v2/id3v2.h
|
||||
mpeg/id3v2/id3v2extendedheader.h
|
||||
mpeg/id3v2/id3v2frame.h
|
||||
mpeg/id3v2/id3v2header.h
|
||||
|
||||
215
3rdparty/taglib/ConfigureChecks.cmake
vendored
Normal file
215
3rdparty/taglib/ConfigureChecks.cmake
vendored
Normal file
@@ -0,0 +1,215 @@
|
||||
include(CheckLibraryExists)
|
||||
include(CheckTypeSize)
|
||||
include(CheckCXXSourceCompiles)
|
||||
|
||||
# Check if the size of numeric types are suitable.
|
||||
|
||||
check_type_size("short" SIZEOF_SHORT)
|
||||
if(NOT ${SIZEOF_SHORT} EQUAL 2)
|
||||
message(FATAL_ERROR "TagLib requires that short is 16-bit wide.")
|
||||
endif()
|
||||
|
||||
check_type_size("int" SIZEOF_INT)
|
||||
if(NOT ${SIZEOF_INT} EQUAL 4)
|
||||
message(FATAL_ERROR "TagLib requires that int is 32-bit wide.")
|
||||
endif()
|
||||
|
||||
check_type_size("long long" SIZEOF_LONGLONG)
|
||||
if(NOT ${SIZEOF_LONGLONG} EQUAL 8)
|
||||
message(FATAL_ERROR "TagLib requires that long long is 64-bit wide.")
|
||||
endif()
|
||||
|
||||
check_type_size("wchar_t" SIZEOF_WCHAR_T)
|
||||
if(${SIZEOF_WCHAR_T} LESS 2)
|
||||
message(FATAL_ERROR "TagLib requires that wchar_t is sufficient to store a UTF-16 char.")
|
||||
endif()
|
||||
|
||||
check_type_size("float" SIZEOF_FLOAT)
|
||||
if(NOT ${SIZEOF_FLOAT} EQUAL 4)
|
||||
message(FATAL_ERROR "TagLib requires that float is 32-bit wide.")
|
||||
endif()
|
||||
|
||||
check_type_size("double" SIZEOF_DOUBLE)
|
||||
if(NOT ${SIZEOF_DOUBLE} EQUAL 8)
|
||||
message(FATAL_ERROR "TagLib requires that double is 64-bit wide.")
|
||||
endif()
|
||||
|
||||
# Determine which kind of atomic operations your compiler supports.
|
||||
|
||||
check_cxx_source_compiles("
|
||||
#include <atomic>
|
||||
int main() {
|
||||
std::atomic_int x(1);
|
||||
++x;
|
||||
--x;
|
||||
return 0;
|
||||
}
|
||||
" HAVE_STD_ATOMIC)
|
||||
|
||||
if(NOT HAVE_STD_ATOMIC)
|
||||
check_cxx_source_compiles("
|
||||
int main() {
|
||||
volatile int x;
|
||||
__sync_add_and_fetch(&x, 1);
|
||||
int y = __sync_sub_and_fetch(&x, 1);
|
||||
return 0;
|
||||
}
|
||||
" HAVE_GCC_ATOMIC)
|
||||
|
||||
if(NOT HAVE_GCC_ATOMIC)
|
||||
check_cxx_source_compiles("
|
||||
#include <libkern/OSAtomic.h>
|
||||
int main() {
|
||||
volatile int32_t x;
|
||||
OSAtomicIncrement32Barrier(&x);
|
||||
int32_t y = OSAtomicDecrement32Barrier(&x);
|
||||
return 0;
|
||||
}
|
||||
" HAVE_MAC_ATOMIC)
|
||||
|
||||
if(NOT HAVE_MAC_ATOMIC)
|
||||
check_cxx_source_compiles("
|
||||
#include <windows.h>
|
||||
int main() {
|
||||
volatile LONG x;
|
||||
InterlockedIncrement(&x);
|
||||
LONG y = InterlockedDecrement(&x);
|
||||
return 0;
|
||||
}
|
||||
" HAVE_WIN_ATOMIC)
|
||||
|
||||
if(NOT HAVE_WIN_ATOMIC)
|
||||
check_cxx_source_compiles("
|
||||
#include <ia64intrin.h>
|
||||
int main() {
|
||||
volatile int x;
|
||||
__sync_add_and_fetch(&x, 1);
|
||||
int y = __sync_sub_and_fetch(&x, 1);
|
||||
return 0;
|
||||
}
|
||||
" HAVE_IA64_ATOMIC)
|
||||
endif()
|
||||
endif()
|
||||
endif()
|
||||
endif()
|
||||
|
||||
# Determine which kind of byte swap functions your compiler supports.
|
||||
|
||||
check_cxx_source_compiles("
|
||||
int main() {
|
||||
__builtin_bswap16(0);
|
||||
__builtin_bswap32(0);
|
||||
__builtin_bswap64(0);
|
||||
return 0;
|
||||
}
|
||||
" HAVE_GCC_BYTESWAP)
|
||||
|
||||
if(NOT HAVE_GCC_BYTESWAP)
|
||||
check_cxx_source_compiles("
|
||||
#include <byteswap.h>
|
||||
int main() {
|
||||
__bswap_16(0);
|
||||
__bswap_32(0);
|
||||
__bswap_64(0);
|
||||
return 0;
|
||||
}
|
||||
" HAVE_GLIBC_BYTESWAP)
|
||||
|
||||
if(NOT HAVE_GLIBC_BYTESWAP)
|
||||
check_cxx_source_compiles("
|
||||
#include <stdlib.h>
|
||||
int main() {
|
||||
_byteswap_ushort(0);
|
||||
_byteswap_ulong(0);
|
||||
_byteswap_uint64(0);
|
||||
return 0;
|
||||
}
|
||||
" HAVE_MSC_BYTESWAP)
|
||||
|
||||
if(NOT HAVE_MSC_BYTESWAP)
|
||||
check_cxx_source_compiles("
|
||||
#include <libkern/OSByteOrder.h>
|
||||
int main() {
|
||||
OSSwapInt16(0);
|
||||
OSSwapInt32(0);
|
||||
OSSwapInt64(0);
|
||||
return 0;
|
||||
}
|
||||
" HAVE_MAC_BYTESWAP)
|
||||
|
||||
if(NOT HAVE_MAC_BYTESWAP)
|
||||
check_cxx_source_compiles("
|
||||
#include <sys/endian.h>
|
||||
int main() {
|
||||
swap16(0);
|
||||
swap32(0);
|
||||
swap64(0);
|
||||
return 0;
|
||||
}
|
||||
" HAVE_OPENBSD_BYTESWAP)
|
||||
endif()
|
||||
endif()
|
||||
endif()
|
||||
endif()
|
||||
|
||||
# Determine whether your compiler supports some safer version of vsprintf.
|
||||
|
||||
check_cxx_source_compiles("
|
||||
#include <cstdio>
|
||||
#include <cstdarg>
|
||||
int main() {
|
||||
char buf[20];
|
||||
va_list args;
|
||||
vsnprintf(buf, 20, \"%d\", args);
|
||||
return 0;
|
||||
}
|
||||
" HAVE_VSNPRINTF)
|
||||
|
||||
if(NOT HAVE_VSNPRINTF)
|
||||
check_cxx_source_compiles("
|
||||
#include <cstdio>
|
||||
#include <cstdarg>
|
||||
int main() {
|
||||
char buf[20];
|
||||
va_list args;
|
||||
vsprintf_s(buf, \"%d\", args);
|
||||
return 0;
|
||||
}
|
||||
" HAVE_VSPRINTF_S)
|
||||
endif()
|
||||
|
||||
# Determine whether your compiler supports ISO _strdup.
|
||||
|
||||
check_cxx_source_compiles("
|
||||
#include <cstring>
|
||||
int main() {
|
||||
_strdup(0);
|
||||
return 0;
|
||||
}
|
||||
" HAVE_ISO_STRDUP)
|
||||
|
||||
# Determine whether zlib is installed.
|
||||
|
||||
if(NOT ZLIB_SOURCE)
|
||||
find_package(ZLIB)
|
||||
if(ZLIB_FOUND)
|
||||
set(HAVE_ZLIB 1)
|
||||
else()
|
||||
set(HAVE_ZLIB 0)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
# Determine whether CppUnit is installed.
|
||||
|
||||
if(BUILD_TESTS AND NOT BUILD_SHARED_LIBS)
|
||||
find_package(CppUnit)
|
||||
if(NOT CppUnit_FOUND)
|
||||
message(STATUS "CppUnit not found, disabling tests.")
|
||||
set(BUILD_TESTS OFF)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
# Detect WinRT mode
|
||||
if(CMAKE_SYSTEM_NAME STREQUAL "WindowsStore")
|
||||
set(PLATFORM WINRT 1)
|
||||
endif()
|
||||
8
3rdparty/taglib/ape/apefile.cpp
vendored
8
3rdparty/taglib/ape/apefile.cpp
vendored
@@ -44,7 +44,7 @@
|
||||
#include "apetag.h"
|
||||
#include "apefooter.h"
|
||||
|
||||
using namespace TagLib;
|
||||
using namespace Strawberry_TagLib::TagLib;
|
||||
|
||||
namespace
|
||||
{
|
||||
@@ -100,7 +100,7 @@ bool APE::File::isSupported(IOStream *stream)
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
APE::File::File(FileName file, bool readProperties, Properties::ReadStyle) :
|
||||
TagLib::File(file),
|
||||
Strawberry_TagLib::TagLib::File(file),
|
||||
d(new FilePrivate())
|
||||
{
|
||||
if(isOpen())
|
||||
@@ -108,7 +108,7 @@ APE::File::File(FileName file, bool readProperties, Properties::ReadStyle) :
|
||||
}
|
||||
|
||||
APE::File::File(IOStream *stream, bool readProperties, Properties::ReadStyle) :
|
||||
TagLib::File(stream),
|
||||
Strawberry_TagLib::TagLib::File(stream),
|
||||
d(new FilePrivate())
|
||||
{
|
||||
if(isOpen())
|
||||
@@ -120,7 +120,7 @@ APE::File::~File()
|
||||
delete d;
|
||||
}
|
||||
|
||||
TagLib::Tag *APE::File::tag() const
|
||||
Strawberry_TagLib::TagLib::Tag *APE::File::tag() const
|
||||
{
|
||||
return &d->tag;
|
||||
}
|
||||
|
||||
6
3rdparty/taglib/ape/apefile.h
vendored
6
3rdparty/taglib/ape/apefile.h
vendored
@@ -38,6 +38,7 @@
|
||||
#include "taglib_export.h"
|
||||
#include "apeproperties.h"
|
||||
|
||||
namespace Strawberry_TagLib {
|
||||
namespace TagLib {
|
||||
|
||||
class Tag;
|
||||
@@ -65,7 +66,7 @@ namespace TagLib {
|
||||
* information specific to APE files.
|
||||
*/
|
||||
|
||||
class TAGLIB_EXPORT File : public TagLib::File
|
||||
class TAGLIB_EXPORT File : public Strawberry_TagLib::TagLib::File
|
||||
{
|
||||
public:
|
||||
/*!
|
||||
@@ -113,7 +114,7 @@ namespace TagLib {
|
||||
* Returns the Tag for this file. This will be an APE tag, an ID3v1 tag
|
||||
* or a combination of the two.
|
||||
*/
|
||||
virtual TagLib::Tag *tag() const;
|
||||
virtual Strawberry_TagLib::TagLib::Tag *tag() const;
|
||||
|
||||
/*!
|
||||
* Implements the unified property interface -- export function.
|
||||
@@ -231,5 +232,6 @@ namespace TagLib {
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
2
3rdparty/taglib/ape/apefooter.cpp
vendored
2
3rdparty/taglib/ape/apefooter.cpp
vendored
@@ -32,7 +32,7 @@
|
||||
|
||||
#include "apefooter.h"
|
||||
|
||||
using namespace TagLib;
|
||||
using namespace Strawberry_TagLib::TagLib;
|
||||
using namespace APE;
|
||||
|
||||
class APE::Footer::FooterPrivate
|
||||
|
||||
2
3rdparty/taglib/ape/apefooter.h
vendored
2
3rdparty/taglib/ape/apefooter.h
vendored
@@ -29,6 +29,7 @@
|
||||
#include "tbytevector.h"
|
||||
#include "taglib_export.h"
|
||||
|
||||
namespace Strawberry_TagLib {
|
||||
namespace TagLib {
|
||||
|
||||
namespace APE {
|
||||
@@ -169,5 +170,6 @@ namespace TagLib {
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
2
3rdparty/taglib/ape/apeitem.cpp
vendored
2
3rdparty/taglib/ape/apeitem.cpp
vendored
@@ -28,7 +28,7 @@
|
||||
|
||||
#include "apeitem.h"
|
||||
|
||||
using namespace TagLib;
|
||||
using namespace Strawberry_TagLib::TagLib;
|
||||
using namespace APE;
|
||||
|
||||
class APE::Item::ItemPrivate
|
||||
|
||||
2
3rdparty/taglib/ape/apeitem.h
vendored
2
3rdparty/taglib/ape/apeitem.h
vendored
@@ -30,6 +30,7 @@
|
||||
#include "tstring.h"
|
||||
#include "tstringlist.h"
|
||||
|
||||
namespace Strawberry_TagLib {
|
||||
namespace TagLib {
|
||||
|
||||
namespace APE {
|
||||
@@ -218,6 +219,7 @@ namespace TagLib {
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
2
3rdparty/taglib/ape/apeproperties.cpp
vendored
2
3rdparty/taglib/ape/apeproperties.cpp
vendored
@@ -36,7 +36,7 @@
|
||||
#include "apetag.h"
|
||||
#include "apefooter.h"
|
||||
|
||||
using namespace TagLib;
|
||||
using namespace Strawberry_TagLib::TagLib;
|
||||
|
||||
class APE::Properties::PropertiesPrivate
|
||||
{
|
||||
|
||||
6
3rdparty/taglib/ape/apeproperties.h
vendored
6
3rdparty/taglib/ape/apeproperties.h
vendored
@@ -33,6 +33,7 @@
|
||||
#include "taglib_export.h"
|
||||
#include "audioproperties.h"
|
||||
|
||||
namespace Strawberry_TagLib {
|
||||
namespace TagLib {
|
||||
|
||||
namespace APE {
|
||||
@@ -55,7 +56,7 @@ namespace TagLib {
|
||||
*
|
||||
* \deprecated
|
||||
*/
|
||||
Properties(File *file, ReadStyle style = Average);
|
||||
TAGLIB_DEPRECATED Properties(File *file, ReadStyle style = Average);
|
||||
|
||||
/*!
|
||||
* Create an instance of APE::Properties with the data read from the
|
||||
@@ -76,7 +77,7 @@ namespace TagLib {
|
||||
*
|
||||
* \deprecated
|
||||
*/
|
||||
virtual int length() const;
|
||||
TAGLIB_DEPRECATED virtual int length() const;
|
||||
|
||||
/*!
|
||||
* Returns the length of the file in seconds. The length is rounded down to
|
||||
@@ -139,5 +140,6 @@ namespace TagLib {
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
8
3rdparty/taglib/ape/apetag.cpp
vendored
8
3rdparty/taglib/ape/apetag.cpp
vendored
@@ -42,7 +42,7 @@
|
||||
#include "apefooter.h"
|
||||
#include "apeitem.h"
|
||||
|
||||
using namespace TagLib;
|
||||
using namespace Strawberry_TagLib::TagLib;
|
||||
using namespace APE;
|
||||
|
||||
namespace
|
||||
@@ -91,13 +91,13 @@ public:
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
APE::Tag::Tag() :
|
||||
TagLib::Tag(),
|
||||
Strawberry_TagLib::TagLib::Tag(),
|
||||
d(new TagPrivate())
|
||||
{
|
||||
}
|
||||
|
||||
APE::Tag::Tag(TagLib::File *file, long footerLocation) :
|
||||
TagLib::Tag(),
|
||||
APE::Tag::Tag(Strawberry_TagLib::TagLib::File *file, long footerLocation) :
|
||||
Strawberry_TagLib::TagLib::Tag(),
|
||||
d(new TagPrivate())
|
||||
{
|
||||
d->file = file;
|
||||
|
||||
6
3rdparty/taglib/ape/apetag.h
vendored
6
3rdparty/taglib/ape/apetag.h
vendored
@@ -34,6 +34,7 @@
|
||||
|
||||
#include "apeitem.h"
|
||||
|
||||
namespace Strawberry_TagLib {
|
||||
namespace TagLib {
|
||||
|
||||
class File;
|
||||
@@ -54,7 +55,7 @@ namespace TagLib {
|
||||
|
||||
//! An APE tag implementation
|
||||
|
||||
class TAGLIB_EXPORT Tag : public TagLib::Tag
|
||||
class TAGLIB_EXPORT Tag : public Strawberry_TagLib::TagLib::Tag
|
||||
{
|
||||
public:
|
||||
/*!
|
||||
@@ -66,7 +67,7 @@ namespace TagLib {
|
||||
* Create an APE tag and parse the data in \a file with APE footer at
|
||||
* \a tagOffset.
|
||||
*/
|
||||
Tag(TagLib::File *file, long footerLocation);
|
||||
Tag(Strawberry_TagLib::TagLib::File *file, long footerLocation);
|
||||
|
||||
/*!
|
||||
* Destroys this Tag instance.
|
||||
@@ -204,5 +205,6 @@ namespace TagLib {
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
2
3rdparty/taglib/asf/asfattribute.cpp
vendored
2
3rdparty/taglib/asf/asfattribute.cpp
vendored
@@ -31,7 +31,7 @@
|
||||
#include "asffile.h"
|
||||
#include "asfutils.h"
|
||||
|
||||
using namespace TagLib;
|
||||
using namespace Strawberry_TagLib::TagLib;
|
||||
|
||||
class ASF::Attribute::AttributePrivate : public RefCounter
|
||||
{
|
||||
|
||||
2
3rdparty/taglib/asf/asfattribute.h
vendored
2
3rdparty/taglib/asf/asfattribute.h
vendored
@@ -31,6 +31,7 @@
|
||||
#include "taglib_export.h"
|
||||
#include "asfpicture.h"
|
||||
|
||||
namespace Strawberry_TagLib {
|
||||
namespace TagLib
|
||||
{
|
||||
|
||||
@@ -204,5 +205,6 @@ namespace TagLib
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
8
3rdparty/taglib/asf/asffile.cpp
vendored
8
3rdparty/taglib/asf/asffile.cpp
vendored
@@ -34,7 +34,7 @@
|
||||
#include "asfproperties.h"
|
||||
#include "asfutils.h"
|
||||
|
||||
using namespace TagLib;
|
||||
using namespace Strawberry_TagLib::TagLib;
|
||||
|
||||
class ASF::File::FilePrivate
|
||||
{
|
||||
@@ -113,7 +113,7 @@ class ASF::File::FilePrivate::UnknownObject : public ASF::File::FilePrivate::Bas
|
||||
{
|
||||
ByteVector myGuid;
|
||||
public:
|
||||
UnknownObject(const ByteVector &guid);
|
||||
explicit UnknownObject(const ByteVector &guid);
|
||||
ByteVector guid() const;
|
||||
};
|
||||
|
||||
@@ -488,7 +488,7 @@ bool ASF::File::isSupported(IOStream *stream)
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
ASF::File::File(FileName file, bool, Properties::ReadStyle) :
|
||||
TagLib::File(file),
|
||||
Strawberry_TagLib::TagLib::File(file),
|
||||
d(new FilePrivate())
|
||||
{
|
||||
if(isOpen())
|
||||
@@ -496,7 +496,7 @@ ASF::File::File(FileName file, bool, Properties::ReadStyle) :
|
||||
}
|
||||
|
||||
ASF::File::File(IOStream *stream, bool, Properties::ReadStyle) :
|
||||
TagLib::File(stream),
|
||||
Strawberry_TagLib::TagLib::File(stream),
|
||||
d(new FilePrivate())
|
||||
{
|
||||
if(isOpen())
|
||||
|
||||
4
3rdparty/taglib/asf/asffile.h
vendored
4
3rdparty/taglib/asf/asffile.h
vendored
@@ -32,6 +32,7 @@
|
||||
#include "asfproperties.h"
|
||||
#include "asftag.h"
|
||||
|
||||
namespace Strawberry_TagLib {
|
||||
namespace TagLib {
|
||||
|
||||
//! An implementation of ASF (WMA) metadata
|
||||
@@ -43,7 +44,7 @@ namespace TagLib {
|
||||
* the abstract TagLib::File API as well as providing some additional
|
||||
* information specific to ASF files.
|
||||
*/
|
||||
class TAGLIB_EXPORT File : public TagLib::File
|
||||
class TAGLIB_EXPORT File : public Strawberry_TagLib::TagLib::File
|
||||
{
|
||||
public:
|
||||
|
||||
@@ -134,5 +135,6 @@ namespace TagLib {
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
2
3rdparty/taglib/asf/asfpicture.cpp
vendored
2
3rdparty/taglib/asf/asfpicture.cpp
vendored
@@ -32,7 +32,7 @@
|
||||
#include "asfpicture.h"
|
||||
#include "asfutils.h"
|
||||
|
||||
using namespace TagLib;
|
||||
using namespace Strawberry_TagLib::TagLib;
|
||||
|
||||
class ASF::Picture::PicturePrivate : public RefCounter
|
||||
{
|
||||
|
||||
2
3rdparty/taglib/asf/asfpicture.h
vendored
2
3rdparty/taglib/asf/asfpicture.h
vendored
@@ -31,6 +31,7 @@
|
||||
#include "taglib_export.h"
|
||||
#include "attachedpictureframe.h"
|
||||
|
||||
namespace Strawberry_TagLib {
|
||||
namespace TagLib
|
||||
{
|
||||
namespace ASF
|
||||
@@ -218,5 +219,6 @@ namespace TagLib
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif // ASFPICTURE_H
|
||||
|
||||
2
3rdparty/taglib/asf/asfproperties.cpp
vendored
2
3rdparty/taglib/asf/asfproperties.cpp
vendored
@@ -27,7 +27,7 @@
|
||||
#include <tstring.h>
|
||||
#include "asfproperties.h"
|
||||
|
||||
using namespace TagLib;
|
||||
using namespace Strawberry_TagLib::TagLib;
|
||||
|
||||
class ASF::Properties::PropertiesPrivate
|
||||
{
|
||||
|
||||
2
3rdparty/taglib/asf/asfproperties.h
vendored
2
3rdparty/taglib/asf/asfproperties.h
vendored
@@ -30,6 +30,7 @@
|
||||
#include "tstring.h"
|
||||
#include "taglib_export.h"
|
||||
|
||||
namespace Strawberry_TagLib {
|
||||
namespace TagLib {
|
||||
|
||||
namespace ASF {
|
||||
@@ -182,5 +183,6 @@ namespace TagLib {
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
6
3rdparty/taglib/asf/asftag.cpp
vendored
6
3rdparty/taglib/asf/asftag.cpp
vendored
@@ -26,7 +26,7 @@
|
||||
#include <tpropertymap.h>
|
||||
#include "asftag.h"
|
||||
|
||||
using namespace TagLib;
|
||||
using namespace Strawberry_TagLib::TagLib;
|
||||
|
||||
class ASF::Tag::TagPrivate
|
||||
{
|
||||
@@ -40,7 +40,7 @@ public:
|
||||
};
|
||||
|
||||
ASF::Tag::Tag() :
|
||||
TagLib::Tag(),
|
||||
Strawberry_TagLib::TagLib::Tag(),
|
||||
d(new TagPrivate())
|
||||
{
|
||||
}
|
||||
@@ -204,7 +204,7 @@ void ASF::Tag::addAttribute(const String &name, const Attribute &attribute)
|
||||
|
||||
bool ASF::Tag::isEmpty() const
|
||||
{
|
||||
return TagLib::Tag::isEmpty() &&
|
||||
return Strawberry_TagLib::TagLib::Tag::isEmpty() &&
|
||||
copyright().isEmpty() &&
|
||||
rating().isEmpty() &&
|
||||
d->attributeListMap.isEmpty();
|
||||
|
||||
5
3rdparty/taglib/asf/asftag.h
vendored
5
3rdparty/taglib/asf/asftag.h
vendored
@@ -32,6 +32,7 @@
|
||||
#include "taglib_export.h"
|
||||
#include "asfattribute.h"
|
||||
|
||||
namespace Strawberry_TagLib {
|
||||
namespace TagLib {
|
||||
|
||||
namespace ASF {
|
||||
@@ -39,7 +40,7 @@ namespace TagLib {
|
||||
typedef List<Attribute> AttributeList;
|
||||
typedef Map<String, AttributeList> AttributeListMap;
|
||||
|
||||
class TAGLIB_EXPORT Tag : public TagLib::Tag {
|
||||
class TAGLIB_EXPORT Tag : public Strawberry_TagLib::TagLib::Tag {
|
||||
|
||||
friend class File;
|
||||
|
||||
@@ -160,6 +161,7 @@ namespace TagLib {
|
||||
* Returns a reference to the item list map. This is an AttributeListMap of
|
||||
* all of the items in the tag.
|
||||
*/
|
||||
// BIC: return by value
|
||||
const AttributeListMap &attributeListMap() const;
|
||||
|
||||
/*!
|
||||
@@ -206,4 +208,5 @@ namespace TagLib {
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
2
3rdparty/taglib/asf/asfutils.h
vendored
2
3rdparty/taglib/asf/asfutils.h
vendored
@@ -30,6 +30,7 @@
|
||||
|
||||
#ifndef DO_NOT_DOCUMENT // tell Doxygen not to document this header
|
||||
|
||||
namespace Strawberry_TagLib {
|
||||
namespace TagLib
|
||||
{
|
||||
namespace ASF
|
||||
@@ -98,6 +99,7 @@ namespace TagLib
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
2
3rdparty/taglib/audioproperties.cpp
vendored
2
3rdparty/taglib/audioproperties.cpp
vendored
@@ -43,7 +43,7 @@
|
||||
|
||||
#include "audioproperties.h"
|
||||
|
||||
using namespace TagLib;
|
||||
using namespace Strawberry_TagLib::TagLib;
|
||||
|
||||
// This macro is a workaround for the fact that we can't add virtual functions.
|
||||
// Should be true virtual functions in taglib2.
|
||||
|
||||
2
3rdparty/taglib/audioproperties.h
vendored
2
3rdparty/taglib/audioproperties.h
vendored
@@ -28,6 +28,7 @@
|
||||
|
||||
#include "taglib_export.h"
|
||||
|
||||
namespace Strawberry_TagLib {
|
||||
namespace TagLib {
|
||||
|
||||
//! A simple, abstract interface to common audio properties
|
||||
@@ -123,5 +124,6 @@ namespace TagLib {
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
35
3rdparty/taglib/config.h.cmake
vendored
Normal file
35
3rdparty/taglib/config.h.cmake
vendored
Normal file
@@ -0,0 +1,35 @@
|
||||
/* config.h. Generated by cmake from config.h.cmake */
|
||||
|
||||
#ifndef TAGLIB_CONFIG_H
|
||||
#define TAGLIB_CONFIG_H
|
||||
|
||||
/* Defined if your compiler supports some byte swap functions */
|
||||
#cmakedefine HAVE_GCC_BYTESWAP 1
|
||||
#cmakedefine HAVE_GLIBC_BYTESWAP 1
|
||||
#cmakedefine HAVE_MSC_BYTESWAP 1
|
||||
#cmakedefine HAVE_MAC_BYTESWAP 1
|
||||
#cmakedefine HAVE_OPENBSD_BYTESWAP 1
|
||||
|
||||
/* Defined if your compiler supports some atomic operations */
|
||||
#cmakedefine HAVE_STD_ATOMIC 1
|
||||
#cmakedefine HAVE_GCC_ATOMIC 1
|
||||
#cmakedefine HAVE_MAC_ATOMIC 1
|
||||
#cmakedefine HAVE_WIN_ATOMIC 1
|
||||
#cmakedefine HAVE_IA64_ATOMIC 1
|
||||
|
||||
/* Defined if your compiler supports some safer version of vsprintf */
|
||||
#cmakedefine HAVE_VSNPRINTF 1
|
||||
#cmakedefine HAVE_VSPRINTF_S 1
|
||||
|
||||
/* Defined if your compiler supports ISO _strdup */
|
||||
#cmakedefine HAVE_ISO_STRDUP 1
|
||||
|
||||
/* Defined if zlib is installed */
|
||||
#cmakedefine HAVE_ZLIB 1
|
||||
|
||||
/* Indicates whether debug messages are shown even in release mode */
|
||||
#cmakedefine TRACE_IN_RELEASE 1
|
||||
|
||||
#cmakedefine TESTS_DIR "@TESTS_DIR@"
|
||||
|
||||
#endif
|
||||
4
3rdparty/taglib/dsdiff/dsdiffdiintag.cpp
vendored
4
3rdparty/taglib/dsdiff/dsdiffdiintag.cpp
vendored
@@ -27,7 +27,7 @@
|
||||
#include "tstringlist.h"
|
||||
#include "tpropertymap.h"
|
||||
|
||||
using namespace TagLib;
|
||||
using namespace Strawberry_TagLib::TagLib;
|
||||
using namespace DSDIFF::DIIN;
|
||||
|
||||
class DSDIFF::DIIN::Tag::TagPrivate
|
||||
@@ -41,7 +41,7 @@ public:
|
||||
String artist;
|
||||
};
|
||||
|
||||
DSDIFF::DIIN::Tag::Tag() : TagLib::Tag()
|
||||
DSDIFF::DIIN::Tag::Tag() : Strawberry_TagLib::TagLib::Tag()
|
||||
{
|
||||
d = new TagPrivate;
|
||||
}
|
||||
|
||||
4
3rdparty/taglib/dsdiff/dsdiffdiintag.h
vendored
4
3rdparty/taglib/dsdiff/dsdiffdiintag.h
vendored
@@ -28,6 +28,7 @@
|
||||
|
||||
#include "tag.h"
|
||||
|
||||
namespace Strawberry_TagLib {
|
||||
namespace TagLib {
|
||||
|
||||
namespace DSDIFF {
|
||||
@@ -39,7 +40,7 @@ namespace TagLib {
|
||||
*
|
||||
* Only Title and Artist tags are supported
|
||||
*/
|
||||
class TAGLIB_EXPORT Tag : public TagLib::Tag
|
||||
class TAGLIB_EXPORT Tag : public Strawberry_TagLib::TagLib::Tag
|
||||
{
|
||||
public:
|
||||
Tag();
|
||||
@@ -145,6 +146,7 @@ namespace TagLib {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
393
3rdparty/taglib/dsdiff/dsdifffile.cpp
vendored
393
3rdparty/taglib/dsdiff/dsdifffile.cpp
vendored
@@ -33,18 +33,45 @@
|
||||
#include "tagunion.h"
|
||||
#include "dsdifffile.h"
|
||||
|
||||
using namespace TagLib;
|
||||
#include <array>
|
||||
|
||||
struct Chunk64
|
||||
{
|
||||
ByteVector name;
|
||||
unsigned long long offset;
|
||||
unsigned long long size;
|
||||
char padding;
|
||||
};
|
||||
using namespace Strawberry_TagLib::TagLib;
|
||||
|
||||
namespace
|
||||
{
|
||||
struct Chunk64
|
||||
{
|
||||
ByteVector name;
|
||||
unsigned long long offset;
|
||||
unsigned long long size;
|
||||
char padding;
|
||||
};
|
||||
|
||||
typedef std::vector<Chunk64> ChunkList;
|
||||
|
||||
int chunkIndex(const ChunkList &chunks, const ByteVector &id)
|
||||
{
|
||||
for(int i = 0; i < chunks.size(); i++) {
|
||||
if(chunks[i].name == id)
|
||||
return i;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
bool isValidChunkID(const ByteVector &name)
|
||||
{
|
||||
if(name.size() != 4)
|
||||
return false;
|
||||
|
||||
for(int i = 0; i < 4; i++) {
|
||||
if(name[i] < 32 || name[i] > 127)
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
enum {
|
||||
ID3v2Index = 0,
|
||||
DIINIndex = 1
|
||||
@@ -59,14 +86,14 @@ class DSDIFF::File::FilePrivate
|
||||
{
|
||||
public:
|
||||
FilePrivate() :
|
||||
endianness(BigEndian),
|
||||
size(0),
|
||||
isID3InPropChunk(false),
|
||||
duplicateID3V2chunkIndex(-1),
|
||||
properties(0),
|
||||
id3v2TagChunkID("ID3 "),
|
||||
hasID3v2(false),
|
||||
hasDiin(false)
|
||||
endianness(BigEndian),
|
||||
size(0),
|
||||
isID3InPropChunk(false),
|
||||
duplicateID3V2chunkIndex(-1),
|
||||
properties(0),
|
||||
id3v2TagChunkID("ID3 "),
|
||||
hasID3v2(false),
|
||||
hasDiin(false)
|
||||
{
|
||||
childChunkIndex[ID3v2Index] = -1;
|
||||
childChunkIndex[DIINIndex] = -1;
|
||||
@@ -81,12 +108,18 @@ public:
|
||||
ByteVector type;
|
||||
unsigned long long size;
|
||||
ByteVector format;
|
||||
std::vector<Chunk64> chunks;
|
||||
std::vector<Chunk64> childChunks[2];
|
||||
int childChunkIndex[2];
|
||||
bool isID3InPropChunk; // Two possibilities can be found: ID3V2 chunk inside PROP chunk or at root level
|
||||
int duplicateID3V2chunkIndex; // 2 ID3 chunks are present. This is then the index of the one in
|
||||
// PROP chunk that will be removed upon next save to remove duplicates.
|
||||
ChunkList chunks;
|
||||
std::array<ChunkList, 2> childChunks;
|
||||
std::array<int, 2> childChunkIndex;
|
||||
/*
|
||||
* Two possibilities can be found: ID3V2 chunk inside PROP chunk or at root level
|
||||
*/
|
||||
bool isID3InPropChunk;
|
||||
/*
|
||||
* ID3 chunks are present. This is then the index of the one in PROP chunk that
|
||||
* will be removed upon next save to remove duplicates.
|
||||
*/
|
||||
int duplicateID3V2chunkIndex;
|
||||
|
||||
Properties *properties;
|
||||
|
||||
@@ -115,7 +148,7 @@ bool DSDIFF::File::isSupported(IOStream *stream)
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
DSDIFF::File::File(FileName file, bool readProperties,
|
||||
Properties::ReadStyle propertiesStyle) : TagLib::File(file)
|
||||
Properties::ReadStyle propertiesStyle) : Strawberry_TagLib::TagLib::File(file)
|
||||
{
|
||||
d = new FilePrivate;
|
||||
d->endianness = BigEndian;
|
||||
@@ -124,7 +157,7 @@ DSDIFF::File::File(FileName file, bool readProperties,
|
||||
}
|
||||
|
||||
DSDIFF::File::File(IOStream *stream, bool readProperties,
|
||||
Properties::ReadStyle propertiesStyle) : TagLib::File(stream)
|
||||
Properties::ReadStyle propertiesStyle) : Strawberry_TagLib::TagLib::File(stream)
|
||||
{
|
||||
d = new FilePrivate;
|
||||
d->endianness = BigEndian;
|
||||
@@ -137,14 +170,14 @@ DSDIFF::File::~File()
|
||||
delete d;
|
||||
}
|
||||
|
||||
TagLib::Tag *DSDIFF::File::tag() const
|
||||
Strawberry_TagLib::TagLib::Tag *DSDIFF::File::tag() const
|
||||
{
|
||||
return &d->tag;
|
||||
}
|
||||
|
||||
ID3v2::Tag *DSDIFF::File::ID3v2Tag() const
|
||||
ID3v2::Tag *DSDIFF::File::ID3v2Tag(bool create) const
|
||||
{
|
||||
return d->tag.access<ID3v2::Tag>(ID3v2Index, false);
|
||||
return d->tag.access<ID3v2::Tag>(ID3v2Index, create);
|
||||
}
|
||||
|
||||
bool DSDIFF::File::hasID3v2Tag() const
|
||||
@@ -152,9 +185,9 @@ bool DSDIFF::File::hasID3v2Tag() const
|
||||
return d->hasID3v2;
|
||||
}
|
||||
|
||||
DSDIFF::DIIN::Tag *DSDIFF::File::DIINTag() const
|
||||
DSDIFF::DIIN::Tag *DSDIFF::File::DIINTag(bool create) const
|
||||
{
|
||||
return d->tag.access<DSDIFF::DIIN::Tag>(DIINIndex, false);
|
||||
return d->tag.access<DSDIFF::DIIN::Tag>(DIINIndex, create);
|
||||
}
|
||||
|
||||
bool DSDIFF::File::hasDIINTag() const
|
||||
@@ -190,6 +223,11 @@ DSDIFF::Properties *DSDIFF::File::audioProperties() const
|
||||
}
|
||||
|
||||
bool DSDIFF::File::save()
|
||||
{
|
||||
return save(AllTags);
|
||||
}
|
||||
|
||||
bool DSDIFF::File::save(TagTypes tags, StripTags strip, ID3v2::Version version)
|
||||
{
|
||||
if(readOnly()) {
|
||||
debug("DSDIFF::File::save() -- File is read only.");
|
||||
@@ -201,36 +239,44 @@ bool DSDIFF::File::save()
|
||||
return false;
|
||||
}
|
||||
|
||||
//if(strip == StripOthers || strip == StripAll)
|
||||
//File::strip(static_cast<TagTypes>(AllTags & ~tags));
|
||||
|
||||
// First: save ID3V2 chunk
|
||||
|
||||
ID3v2::Tag *id3v2Tag = d->tag.access<ID3v2::Tag>(ID3v2Index, false);
|
||||
if(d->isID3InPropChunk) {
|
||||
if(id3v2Tag != NULL && !id3v2Tag->isEmpty()) {
|
||||
setChildChunkData(d->id3v2TagChunkID, id3v2Tag->render(), PROPChunk);
|
||||
d->hasID3v2 = true;
|
||||
|
||||
if(tags & ID3v2 && id3v2Tag) {
|
||||
if(d->isID3InPropChunk) {
|
||||
if(id3v2Tag && !id3v2Tag->isEmpty()) {
|
||||
setChildChunkData(d->id3v2TagChunkID, id3v2Tag->render(version), PROPChunk);
|
||||
d->hasID3v2 = true;
|
||||
}
|
||||
else {
|
||||
// Empty tag: remove it
|
||||
setChildChunkData(d->id3v2TagChunkID, ByteVector(), PROPChunk);
|
||||
d->hasID3v2 = false;
|
||||
}
|
||||
}
|
||||
else {
|
||||
// Empty tag: remove it
|
||||
setChildChunkData(d->id3v2TagChunkID, ByteVector(), PROPChunk);
|
||||
d->hasID3v2 = false;
|
||||
}
|
||||
}
|
||||
else {
|
||||
if(id3v2Tag != NULL && !id3v2Tag->isEmpty()) {
|
||||
setRootChunkData(d->id3v2TagChunkID, id3v2Tag->render());
|
||||
d->hasID3v2 = true;
|
||||
}
|
||||
else {
|
||||
// Empty tag: remove it
|
||||
setRootChunkData(d->id3v2TagChunkID, ByteVector());
|
||||
d->hasID3v2 = false;
|
||||
if(id3v2Tag && !id3v2Tag->isEmpty()) {
|
||||
setRootChunkData(d->id3v2TagChunkID, id3v2Tag->render(version));
|
||||
d->hasID3v2 = true;
|
||||
}
|
||||
else {
|
||||
// Empty tag: remove it
|
||||
setRootChunkData(d->id3v2TagChunkID, ByteVector());
|
||||
d->hasID3v2 = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Second: save the DIIN chunk
|
||||
if(d->hasDiin) {
|
||||
DSDIFF::DIIN::Tag *diinTag = d->tag.access<DSDIFF::DIIN::Tag>(DIINIndex, false);
|
||||
|
||||
if(!diinTag->title().isNull() && !diinTag->title().isEmpty()) {
|
||||
DSDIFF::DIIN::Tag *diinTag = d->tag.access<DSDIFF::DIIN::Tag>(DIINIndex, false);
|
||||
|
||||
if(tags & DIIN && diinTag) {
|
||||
if(!diinTag->title().isEmpty()) {
|
||||
ByteVector diinTitle;
|
||||
diinTitle.append(ByteVector::fromUInt(diinTag->title().size(), d->endianness == BigEndian));
|
||||
diinTitle.append(ByteVector::fromCString(diinTag->title().toCString()));
|
||||
@@ -239,7 +285,7 @@ bool DSDIFF::File::save()
|
||||
else
|
||||
setChildChunkData("DITI", ByteVector(), DIINChunk);
|
||||
|
||||
if(!diinTag->artist().isNull() && !diinTag->artist().isEmpty()) {
|
||||
if(!diinTag->artist().isEmpty()) {
|
||||
ByteVector diinArtist;
|
||||
diinArtist.append(ByteVector::fromUInt(diinTag->artist().size(), d->endianness == BigEndian));
|
||||
diinArtist.append(ByteVector::fromCString(diinTag->artist().toCString()));
|
||||
@@ -258,46 +304,80 @@ bool DSDIFF::File::save()
|
||||
return true;
|
||||
}
|
||||
|
||||
void DSDIFF::File::strip(TagTypes tags)
|
||||
{
|
||||
if(tags & ID3v2) {
|
||||
removeRootChunk("ID3 ");
|
||||
removeRootChunk("id3 ");
|
||||
d->hasID3v2 = false;
|
||||
d->tag.set(ID3v2Index, new ID3v2::Tag);
|
||||
|
||||
/// TODO: needs to also account for ID3v2 tags under the PROP chunk
|
||||
}
|
||||
if(tags & DIIN) {
|
||||
removeRootChunk("DITI");
|
||||
removeRootChunk("DIAR");
|
||||
d->hasDiin = false;
|
||||
d->tag.set(DIINIndex, new DIIN::Tag);
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// private members
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void DSDIFF::File::setRootChunkData(unsigned int i, const ByteVector &data)
|
||||
void DSDIFF::File::removeRootChunk(unsigned int i)
|
||||
{
|
||||
if(data.isNull() || data.isEmpty()) {
|
||||
// Null data: remove chunk
|
||||
// Update global size
|
||||
unsigned long long removedChunkTotalSize = d->chunks[i].size + d->chunks[i].padding + 12;
|
||||
d->size -= removedChunkTotalSize;
|
||||
unsigned long long chunkSize = d->chunks[i].size + d->chunks[i].padding + 12;
|
||||
|
||||
d->size -= chunkSize;
|
||||
insert(ByteVector::fromLongLong(d->size, d->endianness == BigEndian), 4, 8);
|
||||
|
||||
removeBlock(d->chunks[i].offset - 12, removedChunkTotalSize);
|
||||
removeBlock(d->chunks[i].offset - 12, chunkSize);
|
||||
|
||||
// Update the internal offsets
|
||||
|
||||
for(unsigned long r = i + 1; r < d->chunks.size(); r++)
|
||||
d->chunks[r].offset = d->chunks[r - 1].offset + 12
|
||||
+ d->chunks[r - 1].size + d->chunks[r - 1].padding;
|
||||
|
||||
d->chunks.erase(d->chunks.begin() + i);
|
||||
}
|
||||
|
||||
void DSDIFF::File::removeRootChunk(const ByteVector &id)
|
||||
{
|
||||
int i = chunkIndex(d->chunks, id);
|
||||
|
||||
if(i >= 0)
|
||||
removeRootChunk(i);
|
||||
}
|
||||
|
||||
void DSDIFF::File::setRootChunkData(unsigned int i, const ByteVector &data)
|
||||
{
|
||||
if(data.isEmpty()) {
|
||||
removeRootChunk(i);
|
||||
return;
|
||||
}
|
||||
else {
|
||||
// Non null data: update chunk
|
||||
// First we update the global size
|
||||
d->size += ((data.size() + 1) & ~1) - (d->chunks[i].size + d->chunks[i].padding);
|
||||
insert(ByteVector::fromLongLong(d->size, d->endianness == BigEndian), 4, 8);
|
||||
|
||||
// Now update the specific chunk
|
||||
writeChunk(d->chunks[i].name,
|
||||
data,
|
||||
d->chunks[i].offset - 12,
|
||||
d->chunks[i].size + d->chunks[i].padding + 12);
|
||||
// Non null data: update chunk
|
||||
// First we update the global size
|
||||
|
||||
d->chunks[i].size = data.size();
|
||||
d->chunks[i].padding = (data.size() & 0x01) ? 1 : 0;
|
||||
d->size += ((data.size() + 1) & ~1) - (d->chunks[i].size + d->chunks[i].padding);
|
||||
insert(ByteVector::fromLongLong(d->size, d->endianness == BigEndian), 4, 8);
|
||||
|
||||
// Finally update the internal offsets
|
||||
updateRootChunksStructure(i + 1);
|
||||
}
|
||||
// Now update the specific chunk
|
||||
|
||||
writeChunk(d->chunks[i].name,
|
||||
data,
|
||||
d->chunks[i].offset - 12,
|
||||
d->chunks[i].size + d->chunks[i].padding + 12);
|
||||
|
||||
d->chunks[i].size = data.size();
|
||||
d->chunks[i].padding = (data.size() & 0x01) ? 1 : 0;
|
||||
|
||||
// Finally update the internal offsets
|
||||
|
||||
updateRootChunksStructure(i + 1);
|
||||
}
|
||||
|
||||
void DSDIFF::File::setRootChunkData(const ByteVector &name, const ByteVector &data)
|
||||
@@ -307,15 +387,15 @@ void DSDIFF::File::setRootChunkData(const ByteVector &name, const ByteVector &da
|
||||
return;
|
||||
}
|
||||
|
||||
for(unsigned int i = 0; i < d->chunks.size(); i++) {
|
||||
if(d->chunks[i].name == name) {
|
||||
setRootChunkData(i, data);
|
||||
return;
|
||||
}
|
||||
int i = chunkIndex(d->chunks, name);
|
||||
|
||||
if(i >= 0) {
|
||||
setRootChunkData(i, data);
|
||||
return;
|
||||
}
|
||||
|
||||
// Couldn't find an existing chunk, so let's create a new one.
|
||||
unsigned int i = d->chunks.size() - 1;
|
||||
i = d->chunks.size() - 1;
|
||||
unsigned long offset = d->chunks[i].offset + d->chunks[i].size + d->chunks[i].padding;
|
||||
|
||||
// First we update the global size
|
||||
@@ -338,28 +418,29 @@ void DSDIFF::File::setRootChunkData(const ByteVector &name, const ByteVector &da
|
||||
d->chunks.push_back(chunk);
|
||||
}
|
||||
|
||||
void DSDIFF::File::setChildChunkData(unsigned int i,
|
||||
const ByteVector &data,
|
||||
unsigned int childChunkNum)
|
||||
void DSDIFF::File::removeChildChunk(unsigned int i, unsigned int childChunkNum)
|
||||
{
|
||||
std::vector<Chunk64> &childChunks = d->childChunks[childChunkNum];
|
||||
ChunkList &childChunks = d->childChunks[childChunkNum];
|
||||
|
||||
if(data.isNull() || data.isEmpty()) {
|
||||
// Null data: remove chunk
|
||||
// Update global size
|
||||
|
||||
unsigned long long removedChunkTotalSize = childChunks[i].size + childChunks[i].padding + 12;
|
||||
d->size -= removedChunkTotalSize;
|
||||
insert(ByteVector::fromLongLong(d->size, d->endianness == BigEndian), 4, 8);
|
||||
|
||||
// Update child chunk size
|
||||
|
||||
d->chunks[d->childChunkIndex[childChunkNum]].size -= removedChunkTotalSize;
|
||||
insert(ByteVector::fromLongLong(d->chunks[d->childChunkIndex[childChunkNum]].size,
|
||||
d->endianness == BigEndian),
|
||||
d->chunks[d->childChunkIndex[childChunkNum]].offset - 8, 8);
|
||||
// Remove the chunk
|
||||
|
||||
removeBlock(childChunks[i].offset - 12, removedChunkTotalSize);
|
||||
|
||||
// Update the internal offsets
|
||||
// For child chunks
|
||||
|
||||
if((i + 1) < childChunks.size()) {
|
||||
childChunks[i + 1].offset = childChunks[i].offset;
|
||||
i++;
|
||||
@@ -369,49 +450,65 @@ void DSDIFF::File::setChildChunkData(unsigned int i,
|
||||
}
|
||||
|
||||
// And for root chunks
|
||||
|
||||
for(i = d->childChunkIndex[childChunkNum] + 1; i < d->chunks.size(); i++)
|
||||
d->chunks[i].offset = d->chunks[i - 1].offset + 12
|
||||
+ d->chunks[i - 1].size + d->chunks[i - 1].padding;
|
||||
|
||||
childChunks.erase(childChunks.begin() + i);
|
||||
}
|
||||
|
||||
void DSDIFF::File::setChildChunkData(unsigned int i,
|
||||
const ByteVector &data,
|
||||
unsigned int childChunkNum)
|
||||
{
|
||||
ChunkList &childChunks = d->childChunks[childChunkNum];
|
||||
|
||||
if(data.isEmpty()) {
|
||||
removeChildChunk(i, childChunkNum);
|
||||
return;
|
||||
}
|
||||
else {
|
||||
// Non null data: update chunk
|
||||
// First we update the global size
|
||||
d->size += ((data.size() + 1) & ~1) - (childChunks[i].size + childChunks[i].padding);
|
||||
insert(ByteVector::fromLongLong(d->size, d->endianness == BigEndian), 4, 8);
|
||||
// And the PROP chunk size
|
||||
d->chunks[d->childChunkIndex[childChunkNum]].size += ((data.size() + 1) & ~1)
|
||||
- (childChunks[i].size + childChunks[i].padding);
|
||||
insert(ByteVector::fromLongLong(d->chunks[d->childChunkIndex[childChunkNum]].size,
|
||||
d->endianness == BigEndian),
|
||||
d->chunks[d->childChunkIndex[childChunkNum]].offset - 8, 8);
|
||||
|
||||
// Now update the specific chunk
|
||||
writeChunk(childChunks[i].name,
|
||||
data,
|
||||
childChunks[i].offset - 12,
|
||||
childChunks[i].size + childChunks[i].padding + 12);
|
||||
// Non null data: update chunk
|
||||
// First we update the global size
|
||||
|
||||
childChunks[i].size = data.size();
|
||||
childChunks[i].padding = (data.size() & 0x01) ? 1 : 0;
|
||||
d->size += ((data.size() + 1) & ~1) - (childChunks[i].size + childChunks[i].padding);
|
||||
|
||||
// Now update the internal offsets
|
||||
// For child Chunks
|
||||
for(i++; i < childChunks.size(); i++)
|
||||
childChunks[i].offset = childChunks[i - 1].offset + 12
|
||||
+ childChunks[i - 1].size + childChunks[i - 1].padding;
|
||||
insert(ByteVector::fromLongLong(d->size, d->endianness == BigEndian), 4, 8);
|
||||
|
||||
// And for root chunks
|
||||
updateRootChunksStructure(d->childChunkIndex[childChunkNum] + 1);
|
||||
}
|
||||
// And the PROP chunk size
|
||||
|
||||
d->chunks[d->childChunkIndex[childChunkNum]].size +=
|
||||
((data.size() + 1) & ~1) - (childChunks[i].size + childChunks[i].padding);
|
||||
insert(ByteVector::fromLongLong(d->chunks[d->childChunkIndex[childChunkNum]].size,
|
||||
d->endianness == BigEndian),
|
||||
d->chunks[d->childChunkIndex[childChunkNum]].offset - 8, 8);
|
||||
|
||||
// Now update the specific chunk
|
||||
|
||||
writeChunk(childChunks[i].name,
|
||||
data,
|
||||
childChunks[i].offset - 12,
|
||||
childChunks[i].size + childChunks[i].padding + 12);
|
||||
|
||||
childChunks[i].size = data.size();
|
||||
childChunks[i].padding = (data.size() & 0x01) ? 1 : 0;
|
||||
|
||||
// Now update the internal offsets
|
||||
// For child Chunks
|
||||
for(i++; i < childChunks.size(); i++)
|
||||
childChunks[i].offset = childChunks[i - 1].offset + 12
|
||||
+ childChunks[i - 1].size + childChunks[i - 1].padding;
|
||||
|
||||
// And for root chunks
|
||||
updateRootChunksStructure(d->childChunkIndex[childChunkNum] + 1);
|
||||
}
|
||||
|
||||
void DSDIFF::File::setChildChunkData(const ByteVector &name,
|
||||
const ByteVector &data,
|
||||
unsigned int childChunkNum)
|
||||
{
|
||||
std::vector<Chunk64> &childChunks = d->childChunks[childChunkNum];
|
||||
ChunkList &childChunks = d->childChunks[childChunkNum];
|
||||
|
||||
if(childChunks.size() == 0) {
|
||||
debug("DSDIFF::File::setPropChunkData - No valid chunks found.");
|
||||
@@ -426,17 +523,22 @@ void DSDIFF::File::setChildChunkData(const ByteVector &name,
|
||||
}
|
||||
|
||||
// Do not attempt to remove a non existing chunk
|
||||
if(data.isNull() || data.isEmpty())
|
||||
|
||||
if(data.isEmpty())
|
||||
return;
|
||||
|
||||
// Couldn't find an existing chunk, so let's create a new one.
|
||||
|
||||
unsigned int i = childChunks.size() - 1;
|
||||
unsigned long offset = childChunks[i].offset + childChunks[i].size + childChunks[i].padding;
|
||||
|
||||
// First we update the global size
|
||||
|
||||
d->size += (offset & 1) + ((data.size() + 1) & ~1) + 12;
|
||||
insert(ByteVector::fromLongLong(d->size, d->endianness == BigEndian), 4, 8);
|
||||
|
||||
// And the child chunk size
|
||||
|
||||
d->chunks[d->childChunkIndex[childChunkNum]].size += (offset & 1)
|
||||
+ ((data.size() + 1) & ~1) + 12;
|
||||
insert(ByteVector::fromLongLong(d->chunks[d->childChunkIndex[childChunkNum]].size,
|
||||
@@ -444,6 +546,7 @@ void DSDIFF::File::setChildChunkData(const ByteVector &name,
|
||||
d->chunks[d->childChunkIndex[childChunkNum]].offset - 8, 8);
|
||||
|
||||
// Now add the chunk to the file
|
||||
|
||||
unsigned long long nextRootChunkIdx = length();
|
||||
if((d->childChunkIndex[childChunkNum] + 1) < static_cast<int>(d->chunks.size()))
|
||||
nextRootChunkIdx = d->chunks[d->childChunkIndex[childChunkNum] + 1].offset - 12;
|
||||
@@ -453,6 +556,7 @@ void DSDIFF::File::setChildChunkData(const ByteVector &name,
|
||||
(offset & 1) ? 1 : 0);
|
||||
|
||||
// For root chunks
|
||||
|
||||
updateRootChunksStructure(d->childChunkIndex[childChunkNum] + 1);
|
||||
|
||||
Chunk64 chunk;
|
||||
@@ -464,19 +568,6 @@ void DSDIFF::File::setChildChunkData(const ByteVector &name,
|
||||
childChunks.push_back(chunk);
|
||||
}
|
||||
|
||||
static bool isValidChunkID(const ByteVector &name)
|
||||
{
|
||||
if(name.size() != 4)
|
||||
return false;
|
||||
|
||||
for(int i = 0; i < 4; i++) {
|
||||
if(name[i] < 32 || name[i] > 127)
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void DSDIFF::File::updateRootChunksStructure(unsigned int startingChunk)
|
||||
{
|
||||
for(unsigned int i = startingChunk; i < d->chunks.size(); i++)
|
||||
@@ -484,17 +575,19 @@ void DSDIFF::File::updateRootChunksStructure(unsigned int startingChunk)
|
||||
+ d->chunks[i - 1].size + d->chunks[i - 1].padding;
|
||||
|
||||
// Update childchunks structure as well
|
||||
|
||||
if(d->childChunkIndex[PROPChunk] >= static_cast<int>(startingChunk)) {
|
||||
std::vector<Chunk64> &childChunksToUpdate = d->childChunks[PROPChunk];
|
||||
ChunkList &childChunksToUpdate = d->childChunks[PROPChunk];
|
||||
if(childChunksToUpdate.size() > 0) {
|
||||
childChunksToUpdate[0].offset = d->chunks[d->childChunkIndex[PROPChunk]].offset + 12;
|
||||
for(unsigned int i = 1; i < childChunksToUpdate.size(); i++)
|
||||
childChunksToUpdate[i].offset = childChunksToUpdate[i - 1].offset + 12
|
||||
+ childChunksToUpdate[i - 1].size + childChunksToUpdate[i - 1].padding;
|
||||
}
|
||||
|
||||
}
|
||||
if(d->childChunkIndex[DIINChunk] >= static_cast<int>(startingChunk)) {
|
||||
std::vector<Chunk64> &childChunksToUpdate = d->childChunks[DIINChunk];
|
||||
ChunkList &childChunksToUpdate = d->childChunks[DIINChunk];
|
||||
if(childChunksToUpdate.size() > 0) {
|
||||
childChunksToUpdate[0].offset = d->chunks[d->childChunkIndex[DIINChunk]].offset + 12;
|
||||
for(unsigned int i = 1; i < childChunksToUpdate.size(); i++)
|
||||
@@ -513,6 +606,7 @@ void DSDIFF::File::read(bool readProperties, Properties::ReadStyle propertiesSty
|
||||
d->format = readBlock(4);
|
||||
|
||||
// + 12: chunk header at least, fix for additional junk bytes
|
||||
|
||||
while(tell() + 12 <= length()) {
|
||||
ByteVector chunkName = readBlock(4);
|
||||
unsigned long long chunkSize = readBlock(8).toLongLong(bigEndian);
|
||||
@@ -523,7 +617,8 @@ void DSDIFF::File::read(bool readProperties, Properties::ReadStyle propertiesSty
|
||||
break;
|
||||
}
|
||||
|
||||
if(static_cast<unsigned long long>(tell()) + chunkSize > static_cast<unsigned long long>(length())) {
|
||||
if(static_cast<unsigned long long>(tell()) + chunkSize >
|
||||
static_cast<unsigned long long>(length())) {
|
||||
debug("DSDIFF::File::read() -- Chunk '" + chunkName
|
||||
+ "' has invalid size (larger than the file size)");
|
||||
setValid(false);
|
||||
@@ -538,6 +633,7 @@ void DSDIFF::File::read(bool readProperties, Properties::ReadStyle propertiesSty
|
||||
seek(chunk.size, Current);
|
||||
|
||||
// Check padding
|
||||
|
||||
chunk.padding = 0;
|
||||
long uPosNotPadded = tell();
|
||||
if((uPosNotPadded & 0x01) != 0) {
|
||||
@@ -551,10 +647,14 @@ void DSDIFF::File::read(bool readProperties, Properties::ReadStyle propertiesSty
|
||||
d->chunks.push_back(chunk);
|
||||
}
|
||||
|
||||
unsigned long long lengthDSDSamplesTimeChannels = 0; // For DSD uncompressed
|
||||
unsigned long long audioDataSizeinBytes = 0; // For computing bitrate
|
||||
unsigned long dstNumFrames = 0; // For DST compressed frames
|
||||
unsigned short dstFrameRate = 0; // For DST compressed frames
|
||||
// For DSD uncompressed
|
||||
unsigned long long lengthDSDSamplesTimeChannels = 0;
|
||||
// For computing bitrate
|
||||
unsigned long long audioDataSizeinBytes = 0;
|
||||
// For DST compressed frames
|
||||
unsigned long dstNumFrames = 0;
|
||||
// For DST compressed frames
|
||||
unsigned short dstFrameRate = 0;
|
||||
|
||||
for(unsigned int i = 0; i < d->chunks.size(); i++) {
|
||||
if(d->chunks[i].name == "DSD ") {
|
||||
@@ -589,7 +689,8 @@ void DSDIFF::File::read(bool readProperties, Properties::ReadStyle propertiesSty
|
||||
// Found the DST frame information chunk
|
||||
dstNumFrames = readBlock(4).toUInt(bigEndian);
|
||||
dstFrameRate = readBlock(2).toUShort(bigEndian);
|
||||
break; // Found the wanted one, no need to look at the others
|
||||
// Found the wanted one, no need to look at the others
|
||||
break;
|
||||
}
|
||||
|
||||
seek(dstChunkSize, Current);
|
||||
@@ -608,7 +709,8 @@ void DSDIFF::File::read(bool readProperties, Properties::ReadStyle propertiesSty
|
||||
d->childChunkIndex[PROPChunk] = i;
|
||||
// Now decodes the chunks inside the PROP chunk
|
||||
long long propChunkEnd = d->chunks[i].offset + d->chunks[i].size;
|
||||
seek(d->chunks[i].offset + 4); // +4 to remove the 'SND ' marker at beginning of 'PROP' chunk
|
||||
// +4 to remove the 'SND ' marker at beginning of 'PROP' chunk
|
||||
seek(d->chunks[i].offset + 4);
|
||||
while(tell() + 12 <= propChunkEnd) {
|
||||
ByteVector propChunkName = readBlock(4);
|
||||
long long propChunkSize = readBlock(8).toLongLong(bigEndian);
|
||||
@@ -650,7 +752,9 @@ void DSDIFF::File::read(bool readProperties, Properties::ReadStyle propertiesSty
|
||||
else if(d->chunks[i].name == "DIIN") {
|
||||
d->childChunkIndex[DIINChunk] = i;
|
||||
d->hasDiin = true;
|
||||
|
||||
// Now decode the chunks inside the DIIN chunk
|
||||
|
||||
long long diinChunkEnd = d->chunks[i].offset + d->chunks[i].size;
|
||||
seek(d->chunks[i].offset);
|
||||
|
||||
@@ -679,8 +783,10 @@ void DSDIFF::File::read(bool readProperties, Properties::ReadStyle propertiesSty
|
||||
seek(chunk.size, Current);
|
||||
|
||||
// Check padding
|
||||
|
||||
chunk.padding = 0;
|
||||
long uPosNotPadded = tell();
|
||||
|
||||
if((uPosNotPadded & 0x01) != 0) {
|
||||
ByteVector iByte = readBlock(1);
|
||||
if((iByte.size() != 1) || (iByte[0] != 0))
|
||||
@@ -715,10 +821,12 @@ void DSDIFF::File::read(bool readProperties, Properties::ReadStyle propertiesSty
|
||||
unsigned short channels=0;
|
||||
|
||||
for(unsigned int i = 0; i < d->childChunks[PROPChunk].size(); i++) {
|
||||
if(d->childChunks[PROPChunk][i].name == "ID3 " || d->childChunks[PROPChunk][i].name == "id3 ") {
|
||||
if(d->childChunks[PROPChunk][i].name == "ID3 " ||
|
||||
d->childChunks[PROPChunk][i].name == "id3 ") {
|
||||
if(d->hasID3v2) {
|
||||
d->duplicateID3V2chunkIndex = i;
|
||||
continue; // ID3V2 tag has already been found at root level
|
||||
// ID3V2 tag has already been found at root level
|
||||
continue;
|
||||
}
|
||||
d->id3v2TagChunkID = d->childChunks[PROPChunk][i].name;
|
||||
d->tag.set(ID3v2Index, new ID3v2::Tag(this, d->childChunks[PROPChunk][i].offset));
|
||||
@@ -738,6 +846,7 @@ void DSDIFF::File::read(bool readProperties, Properties::ReadStyle propertiesSty
|
||||
}
|
||||
|
||||
// Read title & artist from DIIN chunk
|
||||
|
||||
d->tag.access<DSDIFF::DIIN::Tag>(DIINIndex, true);
|
||||
|
||||
if(d->hasDiin) {
|
||||
@@ -765,8 +874,9 @@ void DSDIFF::File::read(bool readProperties, Properties::ReadStyle propertiesSty
|
||||
if(lengthDSDSamplesTimeChannels == 0) {
|
||||
// DST compressed signal : need to compute length of DSD uncompressed frames
|
||||
if(dstFrameRate > 0)
|
||||
lengthDSDSamplesTimeChannels = (unsigned long long)dstNumFrames
|
||||
* (unsigned long long)sampleRate / (unsigned long long)dstFrameRate;
|
||||
lengthDSDSamplesTimeChannels = (unsigned long long) dstNumFrames *
|
||||
(unsigned long long) sampleRate /
|
||||
(unsigned long long) dstFrameRate;
|
||||
else
|
||||
lengthDSDSamplesTimeChannels = 0;
|
||||
}
|
||||
@@ -777,7 +887,7 @@ void DSDIFF::File::read(bool readProperties, Properties::ReadStyle propertiesSty
|
||||
}
|
||||
int bitrate = 0;
|
||||
if(lengthDSDSamplesTimeChannels > 0)
|
||||
bitrate = (audioDataSizeinBytes*8*sampleRate) / lengthDSDSamplesTimeChannels / 1000;
|
||||
bitrate = (audioDataSizeinBytes * 8 * sampleRate) / lengthDSDSamplesTimeChannels / 1000;
|
||||
|
||||
d->properties = new Properties(sampleRate,
|
||||
channels,
|
||||
@@ -788,7 +898,8 @@ void DSDIFF::File::read(bool readProperties, Properties::ReadStyle propertiesSty
|
||||
|
||||
if(!ID3v2Tag()) {
|
||||
d->tag.access<ID3v2::Tag>(ID3v2Index, true);
|
||||
d->isID3InPropChunk = false; // By default, ID3 chunk is at root level
|
||||
// By default, ID3 chunk is at root level
|
||||
d->isID3InPropChunk = false;
|
||||
d->hasID3v2 = false;
|
||||
}
|
||||
}
|
||||
|
||||
58
3rdparty/taglib/dsdiff/dsdifffile.h
vendored
58
3rdparty/taglib/dsdiff/dsdifffile.h
vendored
@@ -31,6 +31,7 @@
|
||||
#include "dsdiffproperties.h"
|
||||
#include "dsdiffdiintag.h"
|
||||
|
||||
namespace Strawberry_TagLib {
|
||||
namespace TagLib {
|
||||
|
||||
//! An implementation of DSDIFF metadata
|
||||
@@ -40,9 +41,9 @@ namespace TagLib {
|
||||
*
|
||||
* This supports an ID3v2 tag as well as reading stream from the ID3 RIFF
|
||||
* chunk as well as properties from the file.
|
||||
* Description of the DSDIFF format is available
|
||||
* Description of the DSDIFF format is available
|
||||
* at http://dsd-guide.com/sites/default/files/white-papers/DSDIFF_1.5_Spec.pdf
|
||||
* DSDIFF standard does not explictly specify the ID3V2 chunk
|
||||
* DSDIFF standard does not explicitly specify the ID3V2 chunk
|
||||
* It can be found at the root level, but also sometimes inside the PROP chunk
|
||||
* In addition, title and artist info are stored as part of the standard
|
||||
*/
|
||||
@@ -58,9 +59,25 @@ namespace TagLib {
|
||||
* information specific to DSDIFF files.
|
||||
*/
|
||||
|
||||
class TAGLIB_EXPORT File : public TagLib::File
|
||||
class TAGLIB_EXPORT File : public Strawberry_TagLib::TagLib::File
|
||||
{
|
||||
public:
|
||||
|
||||
/*!
|
||||
* This set of flags is used for various operations and is suitable for
|
||||
* being OR-ed together.
|
||||
*/
|
||||
enum TagTypes {
|
||||
//! Empty set. Matches no tag types.
|
||||
NoTags = 0x0000,
|
||||
//! Matches DIIN tags.
|
||||
DIIN = 0x0002,
|
||||
//! Matches ID3v1 tags.
|
||||
ID3v2 = 0x0002,
|
||||
//! Matches all tag types.
|
||||
AllTags = 0xffff
|
||||
};
|
||||
|
||||
/*!
|
||||
* Constructs an DSDIFF file from \a file. If \a readProperties is true
|
||||
* the file's audio properties will also be read.
|
||||
@@ -114,13 +131,13 @@ namespace TagLib {
|
||||
*
|
||||
* \see hasID3v2Tag()
|
||||
*/
|
||||
virtual ID3v2::Tag *ID3v2Tag() const;
|
||||
ID3v2::Tag *ID3v2Tag(bool create = false) const;
|
||||
|
||||
/*!
|
||||
* Returns the DSDIFF DIIN Tag for this file
|
||||
*
|
||||
*/
|
||||
DSDIFF::DIIN::Tag *DIINTag() const;
|
||||
DSDIFF::DIIN::Tag *DIINTag(bool create = false) const;
|
||||
|
||||
/*!
|
||||
* Implements the unified property interface -- export function.
|
||||
@@ -160,15 +177,21 @@ namespace TagLib {
|
||||
virtual bool save();
|
||||
|
||||
/*!
|
||||
* Save the file. This will attempt to save all of the tag types that are
|
||||
* specified by OR-ing together TagTypes values. The save() method above
|
||||
* uses AllTags. This returns true if saving was successful.
|
||||
*
|
||||
* This strips all tags not included in the mask, but does not modify them
|
||||
* in memory, so later calls to save() which make use of these tags will
|
||||
* remain valid. This also strips empty tags.
|
||||
* Save the file. If \a strip is specified, it is possible to choose if
|
||||
* tags not specified in \a tags should be stripped from the file or
|
||||
* retained. With \a version, it is possible to specify whether ID3v2.4
|
||||
* or ID3v2.3 should be used.
|
||||
*/
|
||||
bool save(int tags);
|
||||
bool save(TagTypes tags, StripTags strip = StripOthers, ID3v2::Version version = ID3v2::v4);
|
||||
|
||||
/*!
|
||||
* This will strip the tags that match the OR-ed together TagTypes from the
|
||||
* file. By default it strips all tags. It returns true if the tags are
|
||||
* successfully stripped.
|
||||
*
|
||||
* \note This will update the file immediately.
|
||||
*/
|
||||
void strip(TagTypes tags = AllTags);
|
||||
|
||||
/*!
|
||||
* Returns whether or not the file on disk actually has an ID3v2 tag.
|
||||
@@ -178,8 +201,8 @@ namespace TagLib {
|
||||
bool hasID3v2Tag() const;
|
||||
|
||||
/*!
|
||||
* Returns whether or not the file on disk actually has the DSDIFF
|
||||
* Title & Artist tag.
|
||||
* Returns whether or not the file on disk actually has the DSDIFF
|
||||
* title and artist tags.
|
||||
*
|
||||
* \see DIINTag()
|
||||
*/
|
||||
@@ -204,6 +227,10 @@ namespace TagLib {
|
||||
File(const File &);
|
||||
File &operator=(const File &);
|
||||
|
||||
void removeRootChunk(const ByteVector &id);
|
||||
void removeRootChunk(unsigned int chunk);
|
||||
void removeChildChunk(unsigned int i, unsigned int chunk);
|
||||
|
||||
/*!
|
||||
* Sets the data for the the specified chunk at root level to \a data.
|
||||
*
|
||||
@@ -255,6 +282,7 @@ namespace TagLib {
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
2
3rdparty/taglib/dsdiff/dsdiffproperties.cpp
vendored
2
3rdparty/taglib/dsdiff/dsdiffproperties.cpp
vendored
@@ -28,7 +28,7 @@
|
||||
|
||||
#include "dsdiffproperties.h"
|
||||
|
||||
using namespace TagLib;
|
||||
using namespace Strawberry_TagLib::TagLib;
|
||||
|
||||
class DSDIFF::Properties::PropertiesPrivate
|
||||
{
|
||||
|
||||
2
3rdparty/taglib/dsdiff/dsdiffproperties.h
vendored
2
3rdparty/taglib/dsdiff/dsdiffproperties.h
vendored
@@ -28,6 +28,7 @@
|
||||
|
||||
#include "audioproperties.h"
|
||||
|
||||
namespace Strawberry_TagLib {
|
||||
namespace TagLib {
|
||||
|
||||
namespace DSDIFF {
|
||||
@@ -78,6 +79,7 @@ namespace TagLib {
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
12
3rdparty/taglib/dsf/dsffile.cpp
vendored
12
3rdparty/taglib/dsf/dsffile.cpp
vendored
@@ -32,7 +32,7 @@
|
||||
|
||||
#include "dsffile.h"
|
||||
|
||||
using namespace TagLib;
|
||||
using namespace Strawberry_TagLib::TagLib;
|
||||
|
||||
// The DSF specification is located at http://dsd-guide.com/sites/default/files/white-papers/DSFFileFormatSpec_E.pdf
|
||||
|
||||
@@ -40,8 +40,10 @@ class DSF::File::FilePrivate
|
||||
{
|
||||
public:
|
||||
FilePrivate() :
|
||||
properties(0),
|
||||
tag(0)
|
||||
fileSize(0),
|
||||
metadataOffset(0),
|
||||
properties(nullptr),
|
||||
tag(nullptr)
|
||||
{
|
||||
}
|
||||
|
||||
@@ -74,7 +76,7 @@ bool DSF::File::isSupported(IOStream *stream)
|
||||
|
||||
DSF::File::File(FileName file, bool readProperties,
|
||||
Properties::ReadStyle propertiesStyle) :
|
||||
TagLib::File(file),
|
||||
Strawberry_TagLib::TagLib::File(file),
|
||||
d(new FilePrivate())
|
||||
{
|
||||
if(isOpen())
|
||||
@@ -83,7 +85,7 @@ DSF::File::File(FileName file, bool readProperties,
|
||||
|
||||
DSF::File::File(IOStream *stream, bool readProperties,
|
||||
Properties::ReadStyle propertiesStyle) :
|
||||
TagLib::File(stream),
|
||||
Strawberry_TagLib::TagLib::File(stream),
|
||||
d(new FilePrivate())
|
||||
{
|
||||
if(isOpen())
|
||||
|
||||
14
3rdparty/taglib/dsf/dsffile.h
vendored
14
3rdparty/taglib/dsf/dsffile.h
vendored
@@ -30,6 +30,7 @@
|
||||
#include "id3v2tag.h"
|
||||
#include "dsfproperties.h"
|
||||
|
||||
namespace Strawberry_TagLib {
|
||||
namespace TagLib {
|
||||
|
||||
//! An implementation of DSF metadata
|
||||
@@ -42,20 +43,20 @@ namespace TagLib {
|
||||
|
||||
namespace DSF {
|
||||
|
||||
//! An implementation of TagLib::File with DSF specific methods
|
||||
//! An implementation of Strawberry_TagLib::TagLib::File with DSF specific methods
|
||||
|
||||
/*!
|
||||
* This implements and provides an interface for DSF files to the
|
||||
* TagLib::Tag and TagLib::AudioProperties interfaces by way of implementing
|
||||
* the abstract TagLib::File API as well as providing some additional
|
||||
* Strawberry_TagLib::TagLib::Tag and Strawberry_TagLib::TagLib::AudioProperties interfaces by way of implementing
|
||||
* the abstract Strawberry_TagLib::TagLib::File API as well as providing some additional
|
||||
* information specific to DSF files.
|
||||
*/
|
||||
|
||||
class TAGLIB_EXPORT File : public TagLib::File
|
||||
class TAGLIB_EXPORT File : public Strawberry_TagLib::TagLib::File
|
||||
{
|
||||
public:
|
||||
/*!
|
||||
* Contructs an DSF file from \a file. If \a readProperties is true the
|
||||
* Constructs an DSF file from \a file. If \a readProperties is true the
|
||||
* file's audio properties will also be read using \a propertiesStyle. If
|
||||
* false, \a propertiesStyle is ignored.
|
||||
*/
|
||||
@@ -63,7 +64,7 @@ namespace TagLib {
|
||||
Properties::ReadStyle propertiesStyle = Properties::Average);
|
||||
|
||||
/*!
|
||||
* Contructs an DSF file from \a file. If \a readProperties is true the
|
||||
* Constructs an DSF file from \a file. If \a readProperties is true the
|
||||
* file's audio properties will also be read using \a propertiesStyle. If
|
||||
* false, \a propertiesStyle is ignored.
|
||||
*/
|
||||
@@ -123,6 +124,7 @@ namespace TagLib {
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
4
3rdparty/taglib/dsf/dsfproperties.cpp
vendored
4
3rdparty/taglib/dsf/dsfproperties.cpp
vendored
@@ -28,7 +28,7 @@
|
||||
|
||||
#include "dsfproperties.h"
|
||||
|
||||
using namespace TagLib;
|
||||
using namespace Strawberry_TagLib::TagLib;
|
||||
|
||||
class DSF::Properties::PropertiesPrivate
|
||||
{
|
||||
@@ -66,7 +66,7 @@ public:
|
||||
// public members
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
DSF::Properties::Properties(const ByteVector &data, ReadStyle style) : TagLib::AudioProperties(style)
|
||||
DSF::Properties::Properties(const ByteVector &data, ReadStyle style) : Strawberry_TagLib::TagLib::AudioProperties(style)
|
||||
{
|
||||
d = new PropertiesPrivate;
|
||||
read(data);
|
||||
|
||||
4
3rdparty/taglib/dsf/dsfproperties.h
vendored
4
3rdparty/taglib/dsf/dsfproperties.h
vendored
@@ -28,6 +28,7 @@
|
||||
|
||||
#include "audioproperties.h"
|
||||
|
||||
namespace Strawberry_TagLib {
|
||||
namespace TagLib {
|
||||
|
||||
namespace DSF {
|
||||
@@ -41,7 +42,7 @@ namespace TagLib {
|
||||
* API.
|
||||
*/
|
||||
|
||||
class TAGLIB_EXPORT Properties : public TagLib::AudioProperties
|
||||
class TAGLIB_EXPORT Properties : public Strawberry_TagLib::TagLib::AudioProperties
|
||||
{
|
||||
public:
|
||||
/*!
|
||||
@@ -87,6 +88,7 @@ namespace TagLib {
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
18
3rdparty/taglib/fileref.cpp
vendored
18
3rdparty/taglib/fileref.cpp
vendored
@@ -55,7 +55,7 @@
|
||||
#include "dsffile.h"
|
||||
#include "dsdifffile.h"
|
||||
|
||||
using namespace TagLib;
|
||||
using namespace Strawberry_TagLib::TagLib;
|
||||
|
||||
namespace
|
||||
{
|
||||
@@ -74,7 +74,7 @@ namespace
|
||||
return file;
|
||||
}
|
||||
|
||||
return 0;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Detect the file type based on the file extension.
|
||||
@@ -98,7 +98,7 @@ namespace
|
||||
// that a default file type resolver is created.
|
||||
|
||||
if(ext.isEmpty())
|
||||
return 0;
|
||||
return nullptr;
|
||||
|
||||
// .oga can be any audio in the Ogg container. So leave it to content-based detection.
|
||||
|
||||
@@ -142,7 +142,7 @@ namespace
|
||||
if(ext == "DSF")
|
||||
return new DSF::File(stream, readAudioProperties, audioPropertiesStyle);
|
||||
|
||||
return 0;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Detect the file type based on the actual content of the stream.
|
||||
@@ -194,7 +194,7 @@ namespace
|
||||
delete file;
|
||||
}
|
||||
|
||||
return 0;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Internal function that supports FileRef::create().
|
||||
@@ -220,7 +220,7 @@ namespace
|
||||
ext = s.substr(pos + 1).upper();
|
||||
|
||||
if(ext.isEmpty())
|
||||
return 0;
|
||||
return nullptr;
|
||||
|
||||
if(ext == "MP3")
|
||||
return new MPEG::File(fileName, ID3v2::FrameFactory::instance(), readAudioProperties, audioPropertiesStyle);
|
||||
@@ -270,7 +270,7 @@ namespace
|
||||
if(ext == "DSF")
|
||||
return new DSF::File(fileName, readAudioProperties, audioPropertiesStyle);
|
||||
|
||||
return 0;
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -335,7 +335,7 @@ Tag *FileRef::tag() const
|
||||
{
|
||||
if(isNull()) {
|
||||
debug("FileRef::tag() - Called without a valid file.");
|
||||
return 0;
|
||||
return nullptr;
|
||||
}
|
||||
return d->file->tag();
|
||||
}
|
||||
@@ -344,7 +344,7 @@ AudioProperties *FileRef::audioProperties() const
|
||||
{
|
||||
if(isNull()) {
|
||||
debug("FileRef::audioProperties() - Called without a valid file.");
|
||||
return 0;
|
||||
return nullptr;
|
||||
}
|
||||
return d->file->audioProperties();
|
||||
}
|
||||
|
||||
8
3rdparty/taglib/fileref.h
vendored
8
3rdparty/taglib/fileref.h
vendored
@@ -32,6 +32,7 @@
|
||||
#include "taglib_export.h"
|
||||
#include "audioproperties.h"
|
||||
|
||||
namespace Strawberry_TagLib {
|
||||
namespace TagLib {
|
||||
|
||||
class Tag;
|
||||
@@ -72,10 +73,10 @@ namespace TagLib {
|
||||
*
|
||||
* class MyFileTypeResolver : FileTypeResolver
|
||||
* {
|
||||
* TagLib::File *createFile(TagLib::FileName *fileName, bool, AudioProperties::ReadStyle) const
|
||||
* Strawberry_TagLib::TagLib::File *createFile(Strawberry_TagLib::TagLib::FileName *fileName, bool, AudioProperties::ReadStyle) const
|
||||
* {
|
||||
* if(someCheckForAnMP3File(fileName))
|
||||
* return new TagLib::MPEG::File(fileName);
|
||||
* return new Strawberry_TagLib::TagLib::MPEG::File(fileName);
|
||||
* return 0;
|
||||
* }
|
||||
* }
|
||||
@@ -282,6 +283,7 @@ namespace TagLib {
|
||||
FileRefPrivate *d;
|
||||
};
|
||||
|
||||
} // namespace TagLib
|
||||
}
|
||||
} // namespace Strawberry_TagLib::TagLib
|
||||
|
||||
#endif
|
||||
|
||||
12
3rdparty/taglib/flac/flacfile.cpp
vendored
12
3rdparty/taglib/flac/flacfile.cpp
vendored
@@ -41,7 +41,7 @@
|
||||
#include "flacmetadatablock.h"
|
||||
#include "flacunknownmetadatablock.h"
|
||||
|
||||
using namespace TagLib;
|
||||
using namespace Strawberry_TagLib::TagLib;
|
||||
|
||||
namespace
|
||||
{
|
||||
@@ -60,7 +60,7 @@ namespace
|
||||
class FLAC::File::FilePrivate
|
||||
{
|
||||
public:
|
||||
FilePrivate(const ID3v2::FrameFactory *frameFactory = ID3v2::FrameFactory::instance()) :
|
||||
explicit FilePrivate(const ID3v2::FrameFactory *frameFactory = ID3v2::FrameFactory::instance()) :
|
||||
ID3v2FrameFactory(frameFactory),
|
||||
ID3v2Location(-1),
|
||||
ID3v2OriginalSize(0),
|
||||
@@ -112,7 +112,7 @@ bool FLAC::File::isSupported(IOStream *stream)
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
FLAC::File::File(FileName file, bool readProperties, Properties::ReadStyle) :
|
||||
TagLib::File(file),
|
||||
Strawberry_TagLib::TagLib::File(file),
|
||||
d(new FilePrivate())
|
||||
{
|
||||
if(isOpen())
|
||||
@@ -121,7 +121,7 @@ FLAC::File::File(FileName file, bool readProperties, Properties::ReadStyle) :
|
||||
|
||||
FLAC::File::File(FileName file, ID3v2::FrameFactory *frameFactory,
|
||||
bool readProperties, Properties::ReadStyle) :
|
||||
TagLib::File(file),
|
||||
Strawberry_TagLib::TagLib::File(file),
|
||||
d(new FilePrivate(frameFactory))
|
||||
{
|
||||
if(isOpen())
|
||||
@@ -130,7 +130,7 @@ FLAC::File::File(FileName file, ID3v2::FrameFactory *frameFactory,
|
||||
|
||||
FLAC::File::File(IOStream *stream, ID3v2::FrameFactory *frameFactory,
|
||||
bool readProperties, Properties::ReadStyle) :
|
||||
TagLib::File(stream),
|
||||
Strawberry_TagLib::TagLib::File(stream),
|
||||
d(new FilePrivate(frameFactory))
|
||||
{
|
||||
if(isOpen())
|
||||
@@ -142,7 +142,7 @@ FLAC::File::~File()
|
||||
delete d;
|
||||
}
|
||||
|
||||
TagLib::Tag *FLAC::File::tag() const
|
||||
Strawberry_TagLib::TagLib::Tag *FLAC::File::tag() const
|
||||
{
|
||||
return &d->tag;
|
||||
}
|
||||
|
||||
12
3rdparty/taglib/flac/flacfile.h
vendored
12
3rdparty/taglib/flac/flacfile.h
vendored
@@ -34,6 +34,7 @@
|
||||
#include "flacpicture.h"
|
||||
#include "flacproperties.h"
|
||||
|
||||
namespace Strawberry_TagLib {
|
||||
namespace TagLib {
|
||||
|
||||
class Tag;
|
||||
@@ -63,7 +64,7 @@ namespace TagLib {
|
||||
* information specific to FLAC files.
|
||||
*/
|
||||
|
||||
class TAGLIB_EXPORT File : public TagLib::File
|
||||
class TAGLIB_EXPORT File : public Strawberry_TagLib::TagLib::File
|
||||
{
|
||||
public:
|
||||
/*!
|
||||
@@ -139,7 +140,7 @@ namespace TagLib {
|
||||
* \see ID3v1Tag()
|
||||
* \see XiphComment()
|
||||
*/
|
||||
virtual TagLib::Tag *tag() const;
|
||||
virtual Strawberry_TagLib::TagLib::Tag *tag() const;
|
||||
|
||||
/*!
|
||||
* Implements the unified property interface -- export function.
|
||||
@@ -240,7 +241,7 @@ namespace TagLib {
|
||||
* \see ID3v2FrameFactory
|
||||
* \deprecated This value should be passed in via the constructor
|
||||
*/
|
||||
void setID3v2FrameFactory(const ID3v2::FrameFactory *factory);
|
||||
TAGLIB_DEPRECATED void setID3v2FrameFactory(const ID3v2::FrameFactory *factory);
|
||||
|
||||
/*!
|
||||
* Returns the block of data used by FLAC::Properties for parsing the
|
||||
@@ -248,7 +249,7 @@ namespace TagLib {
|
||||
*
|
||||
* \deprecated Always returns an empty vector.
|
||||
*/
|
||||
ByteVector streamInfoData(); // BIC: remove
|
||||
TAGLIB_DEPRECATED ByteVector streamInfoData(); // BIC: remove
|
||||
|
||||
/*!
|
||||
* Returns the length of the audio-stream, used by FLAC::Properties for
|
||||
@@ -256,7 +257,7 @@ namespace TagLib {
|
||||
*
|
||||
* \deprecated Always returns zero.
|
||||
*/
|
||||
long streamLength(); // BIC: remove
|
||||
TAGLIB_DEPRECATED long streamLength(); // BIC: remove
|
||||
|
||||
/*!
|
||||
* Returns a list of pictures attached to the FLAC file.
|
||||
@@ -339,5 +340,6 @@ namespace TagLib {
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
2
3rdparty/taglib/flac/flacmetadatablock.cpp
vendored
2
3rdparty/taglib/flac/flacmetadatablock.cpp
vendored
@@ -27,7 +27,7 @@
|
||||
#include <tdebug.h>
|
||||
#include "flacmetadatablock.h"
|
||||
|
||||
using namespace TagLib;
|
||||
using namespace Strawberry_TagLib::TagLib;
|
||||
|
||||
class FLAC::MetadataBlock::MetadataBlockPrivate
|
||||
{
|
||||
|
||||
2
3rdparty/taglib/flac/flacmetadatablock.h
vendored
2
3rdparty/taglib/flac/flacmetadatablock.h
vendored
@@ -30,6 +30,7 @@
|
||||
#include "tbytevector.h"
|
||||
#include "taglib_export.h"
|
||||
|
||||
namespace Strawberry_TagLib {
|
||||
namespace TagLib {
|
||||
|
||||
namespace FLAC {
|
||||
@@ -71,5 +72,6 @@ namespace TagLib {
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
2
3rdparty/taglib/flac/flacpicture.cpp
vendored
2
3rdparty/taglib/flac/flacpicture.cpp
vendored
@@ -27,7 +27,7 @@
|
||||
#include <tdebug.h>
|
||||
#include "flacpicture.h"
|
||||
|
||||
using namespace TagLib;
|
||||
using namespace Strawberry_TagLib::TagLib;
|
||||
|
||||
class FLAC::Picture::PicturePrivate
|
||||
{
|
||||
|
||||
2
3rdparty/taglib/flac/flacpicture.h
vendored
2
3rdparty/taglib/flac/flacpicture.h
vendored
@@ -32,6 +32,7 @@
|
||||
#include "taglib_export.h"
|
||||
#include "flacmetadatablock.h"
|
||||
|
||||
namespace Strawberry_TagLib {
|
||||
namespace TagLib {
|
||||
|
||||
namespace FLAC {
|
||||
@@ -204,5 +205,6 @@ namespace TagLib {
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
2
3rdparty/taglib/flac/flacproperties.cpp
vendored
2
3rdparty/taglib/flac/flacproperties.cpp
vendored
@@ -29,7 +29,7 @@
|
||||
#include "flacproperties.h"
|
||||
#include "flacfile.h"
|
||||
|
||||
using namespace TagLib;
|
||||
using namespace Strawberry_TagLib::TagLib;
|
||||
|
||||
class FLAC::Properties::PropertiesPrivate
|
||||
{
|
||||
|
||||
6
3rdparty/taglib/flac/flacproperties.h
vendored
6
3rdparty/taglib/flac/flacproperties.h
vendored
@@ -29,6 +29,7 @@
|
||||
#include "taglib_export.h"
|
||||
#include "audioproperties.h"
|
||||
|
||||
namespace Strawberry_TagLib {
|
||||
namespace TagLib {
|
||||
|
||||
namespace FLAC {
|
||||
@@ -72,7 +73,7 @@ namespace TagLib {
|
||||
*
|
||||
* \deprecated
|
||||
*/
|
||||
virtual int length() const;
|
||||
TAGLIB_DEPRECATED virtual int length() const;
|
||||
|
||||
/*!
|
||||
* Returns the length of the file in seconds. The length is rounded down to
|
||||
@@ -120,7 +121,7 @@ namespace TagLib {
|
||||
*
|
||||
* \deprecated
|
||||
*/
|
||||
int sampleWidth() const;
|
||||
TAGLIB_DEPRECATED int sampleWidth() const;
|
||||
|
||||
/*!
|
||||
* Return the number of sample frames.
|
||||
@@ -144,5 +145,6 @@ namespace TagLib {
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
@@ -28,7 +28,7 @@
|
||||
#include <tstring.h>
|
||||
#include "flacunknownmetadatablock.h"
|
||||
|
||||
using namespace TagLib;
|
||||
using namespace Strawberry_TagLib::TagLib;
|
||||
|
||||
class FLAC::UnknownMetadataBlock::UnknownMetadataBlockPrivate
|
||||
{
|
||||
|
||||
@@ -31,6 +31,7 @@
|
||||
#include "taglib_export.h"
|
||||
#include "flacmetadatablock.h"
|
||||
|
||||
namespace Strawberry_TagLib {
|
||||
namespace TagLib {
|
||||
|
||||
namespace FLAC {
|
||||
@@ -77,5 +78,6 @@ namespace TagLib {
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
6
3rdparty/taglib/it/itfile.cpp
vendored
6
3rdparty/taglib/it/itfile.cpp
vendored
@@ -30,13 +30,13 @@
|
||||
#include "modfileprivate.h"
|
||||
#include "tpropertymap.h"
|
||||
|
||||
using namespace TagLib;
|
||||
using namespace Strawberry_TagLib::TagLib;
|
||||
using namespace IT;
|
||||
|
||||
class IT::File::FilePrivate
|
||||
{
|
||||
public:
|
||||
FilePrivate(AudioProperties::ReadStyle propertiesStyle)
|
||||
explicit FilePrivate(AudioProperties::ReadStyle propertiesStyle)
|
||||
: tag(), properties(propertiesStyle)
|
||||
{
|
||||
}
|
||||
@@ -277,7 +277,7 @@ void IT::File::read(bool)
|
||||
// in the instrument/sample names and more characters
|
||||
// afterwards. The spec does not mention such a case.
|
||||
// Currently I just discard anything after a nil, but
|
||||
// e.g. VLC seems to interprete a nil as a space. I
|
||||
// e.g. VLC seems to interpret a nil as a space. I
|
||||
// don't know what is the proper behaviour.
|
||||
for(unsigned short i = 0; i < instrumentCount; ++ i) {
|
||||
seek(192L + length + ((long)i << 2));
|
||||
|
||||
2
3rdparty/taglib/it/itfile.h
vendored
2
3rdparty/taglib/it/itfile.h
vendored
@@ -29,6 +29,7 @@
|
||||
#include "modtag.h"
|
||||
#include "itproperties.h"
|
||||
|
||||
namespace Strawberry_TagLib {
|
||||
namespace TagLib {
|
||||
|
||||
namespace IT {
|
||||
@@ -105,5 +106,6 @@ namespace TagLib {
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
2
3rdparty/taglib/it/itproperties.cpp
vendored
2
3rdparty/taglib/it/itproperties.cpp
vendored
@@ -26,7 +26,7 @@
|
||||
|
||||
#include "itproperties.h"
|
||||
|
||||
using namespace TagLib;
|
||||
using namespace Strawberry_TagLib::TagLib;
|
||||
using namespace IT;
|
||||
|
||||
class IT::Properties::PropertiesPrivate
|
||||
|
||||
2
3rdparty/taglib/it/itproperties.h
vendored
2
3rdparty/taglib/it/itproperties.h
vendored
@@ -29,6 +29,7 @@
|
||||
#include "taglib.h"
|
||||
#include "audioproperties.h"
|
||||
|
||||
namespace Strawberry_TagLib {
|
||||
namespace TagLib {
|
||||
namespace IT {
|
||||
class TAGLIB_EXPORT Properties : public AudioProperties {
|
||||
@@ -103,5 +104,6 @@ namespace TagLib {
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
4
3rdparty/taglib/mod/modfile.cpp
vendored
4
3rdparty/taglib/mod/modfile.cpp
vendored
@@ -30,13 +30,13 @@
|
||||
#include "modfileprivate.h"
|
||||
#include "tpropertymap.h"
|
||||
|
||||
using namespace TagLib;
|
||||
using namespace Strawberry_TagLib::TagLib;
|
||||
using namespace Mod;
|
||||
|
||||
class Mod::File::FilePrivate
|
||||
{
|
||||
public:
|
||||
FilePrivate(AudioProperties::ReadStyle propertiesStyle)
|
||||
explicit FilePrivate(AudioProperties::ReadStyle propertiesStyle)
|
||||
: properties(propertiesStyle)
|
||||
{
|
||||
}
|
||||
|
||||
4
3rdparty/taglib/mod/modfile.h
vendored
4
3rdparty/taglib/mod/modfile.h
vendored
@@ -33,11 +33,12 @@
|
||||
#include "modtag.h"
|
||||
#include "modproperties.h"
|
||||
|
||||
namespace Strawberry_TagLib {
|
||||
namespace TagLib {
|
||||
|
||||
namespace Mod {
|
||||
|
||||
class TAGLIB_EXPORT File : public TagLib::Mod::FileBase
|
||||
class TAGLIB_EXPORT File : public Strawberry_TagLib::TagLib::Mod::FileBase
|
||||
{
|
||||
public:
|
||||
/*!
|
||||
@@ -110,5 +111,6 @@ namespace TagLib {
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
6
3rdparty/taglib/mod/modfilebase.cpp
vendored
6
3rdparty/taglib/mod/modfilebase.cpp
vendored
@@ -27,14 +27,14 @@
|
||||
#include "tdebug.h"
|
||||
#include "modfilebase.h"
|
||||
|
||||
using namespace TagLib;
|
||||
using namespace Strawberry_TagLib::TagLib;
|
||||
using namespace Mod;
|
||||
|
||||
Mod::FileBase::FileBase(FileName file) : TagLib::File(file)
|
||||
Mod::FileBase::FileBase(FileName file) : Strawberry_TagLib::TagLib::File(file)
|
||||
{
|
||||
}
|
||||
|
||||
Mod::FileBase::FileBase(IOStream *stream) : TagLib::File(stream)
|
||||
Mod::FileBase::FileBase(IOStream *stream) : Strawberry_TagLib::TagLib::File(stream)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
4
3rdparty/taglib/mod/modfilebase.h
vendored
4
3rdparty/taglib/mod/modfilebase.h
vendored
@@ -34,11 +34,12 @@
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
namespace Strawberry_TagLib {
|
||||
namespace TagLib {
|
||||
|
||||
namespace Mod {
|
||||
|
||||
class TAGLIB_EXPORT FileBase : public TagLib::File
|
||||
class TAGLIB_EXPORT FileBase : public Strawberry_TagLib::TagLib::File
|
||||
{
|
||||
protected:
|
||||
FileBase(FileName file);
|
||||
@@ -62,5 +63,6 @@ namespace TagLib {
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
2
3rdparty/taglib/mod/modproperties.cpp
vendored
2
3rdparty/taglib/mod/modproperties.cpp
vendored
@@ -26,7 +26,7 @@
|
||||
|
||||
#include "modproperties.h"
|
||||
|
||||
using namespace TagLib;
|
||||
using namespace Strawberry_TagLib::TagLib;
|
||||
using namespace Mod;
|
||||
|
||||
class Mod::Properties::PropertiesPrivate
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user