Compare commits

...

483 Commits
0.1.6 ... 0.5.2

Author SHA1 Message Date
Jonas Kvinge
1a8fe18b8e Release 0.5.2 2019-01-26 18:13:04 +01:00
Jonas Kvinge
4599b4a9cc Fix thanks to 2019-01-26 18:10:04 +01:00
Jonas Kvinge
6ab6ab56dd Fix define 2019-01-26 17:56:18 +01:00
Jonas Kvinge
59f5dc21ac Update Changelog 2019-01-26 17:45:52 +01:00
Jonas Kvinge
8f316db49c Add raise() to make sure window is on top 2019-01-26 17:44:05 +01:00
Jonas Kvinge
4c2f28311c Remove commented code 2019-01-26 17:33:47 +01:00
Jonas Kvinge
6ae022fbec Update Changelog 2019-01-26 17:31:05 +01:00
Jonas Kvinge
cfbb0f8fc0 Update copyright 2019-01-26 17:30:54 +01:00
Jonas Kvinge
3ad2c2ad6a Update copyright 2019-01-26 17:30:44 +01:00
Jonas Kvinge
1ce553fb65 Copy album covers to file system devices 2019-01-26 17:18:26 +01:00
Jonas Kvinge
ec5ec83edc Change transcoder output file and add missing suffix 2019-01-26 14:57:25 +01:00
Jonas Kvinge
18087fd797 Update Changlog 2019-01-25 23:46:13 +01:00
Jonas Kvinge
f90de75e3a Add warning when enabling X11 shortcuts on gnome, cinnamon and KDE 2019-01-25 21:36:28 +01:00
Jonas Kvinge
3241815a11 Remove core/tagreaderclient.h include 2019-01-24 20:29:22 +01:00
Jonas Kvinge
db3f8d3b83 Fix uninitialised variables 2019-01-24 20:22:05 +01:00
Jonas Kvinge
9983dc3138 Rename globalshortcuts GSD D-Bus backend to avoid confusion 2019-01-24 20:17:50 +01:00
Jonas Kvinge
ad141c165b Update imobiledevice 2019-01-24 19:43:52 +01:00
Jonas Kvinge
93b252b9e7 Update README.md 2019-01-24 19:42:04 +01:00
Jonas Kvinge
42e245e334 Update README.md 2019-01-24 19:41:21 +01:00
Jonas Kvinge
cb844084e8 Add log to organise error dialog 2019-01-24 19:20:10 +01:00
Jonas Kvinge
3483736490 Fix remove misc from album title 2019-01-24 19:18:23 +01:00
Jonas Kvinge
119a75588e Fix aac mp4 transcoder 2019-01-24 19:16:39 +01:00
Jonas Kvinge
2f42242305 Use vbr by default 2019-01-24 19:14:51 +01:00
Jonas Kvinge
4990f44b10 Save album cover to gpod devices 2019-01-24 19:13:57 +01:00
Jonas Kvinge
85eba24167 Add iLister and AFC device 2019-01-24 19:10:42 +01:00
Jonas Kvinge
ce4be75803 Fix itdb track type 2019-01-24 19:10:10 +01:00
Jonas Kvinge
706529248a Fix find_path check for X11 headers 2019-01-24 18:25:54 +01:00
Jonas Kvinge
79f50f5343 Remove compile warning when missing X11/XF86keysym.h 2019-01-24 18:25:24 +01:00
Jonas Kvinge
7437c208ff Remove remastered from album title 2019-01-22 22:49:48 +01:00
Jonas Kvinge
b838893e88 Remove libQt5Gui-private-headers-devel 2019-01-22 18:16:46 +01:00
Jonas Kvinge
41e2a75675 Add error handling for mtp and gpod device 2019-01-21 18:58:54 +01:00
Jonas Kvinge
ad5e366aad Use ItemToIndex and fix memory leaks in devices 2019-01-21 17:44:37 +01:00
Jonas Kvinge
b35c641df6 Add libgstlibav.so to macdeploy 2019-01-19 22:02:56 +01:00
Jonas Kvinge
22c476d507 Add libgstlame.so to macdeploy (#63) 2019-01-19 18:00:47 +01:00
Jonas Kvinge
8f9b1d708b Update .travis.yml (#62) 2019-01-19 00:55:03 +01:00
Jonas Kvinge
9ce8b60cd2 unset dirs 2019-01-18 01:00:07 +01:00
Jonas Kvinge
9401ad2ba3 Check for libsingleapplication too 2019-01-18 00:40:54 +01:00
Jonas Kvinge
128b6c7ce9 Update Changelog 2019-01-13 14:48:01 +01:00
Jonas Kvinge
04d509f6eb Also do secondary check as a core app 2019-01-13 14:46:22 +01:00
Jonas Kvinge
c91cef3507 Add error handling/message for url handler 2019-01-13 00:06:08 +01:00
Jonas Kvinge
a9304a840f Remove libQt5Gui-private-headers-devel 2019-01-12 17:55:02 +01:00
Jonas Kvinge
3ba500b589 Turn back git revision 2019-01-12 17:45:13 +01:00
Jonas Kvinge
cc6a1ad63b Release 0.5.1 2019-01-12 16:12:29 +01:00
Jonas Kvinge
a4120d3d3e Fix use system SingleApplication 2019-01-12 02:07:39 +01:00
Jonas Kvinge
494ab68dd2 Fix SingleCoreApplication 2019-01-12 02:06:26 +01:00
Jonas Kvinge
708dd12243 Update Changelog 2019-01-11 21:58:24 +01:00
Jonas Kvinge
49601423cf Fix scroll over icon to change track feature 2019-01-11 20:21:45 +01:00
Jonas Kvinge
928aebd439 Move check for secondary instance 2019-01-11 20:07:59 +01:00
Jonas Kvinge
97717929dd Update README.md 2019-01-11 01:11:45 +01:00
Jonas Kvinge
c94f9073f8 Add SingleCoreApplication 2019-01-11 01:04:13 +01:00
Jonas Kvinge
5746ee74cf Fix infinite loop in StyleSheetLoader 2019-01-10 20:07:17 +01:00
Jonas Kvinge
970dcaf0fd Adjust about dialog and make text selectable 2019-01-09 22:06:15 +01:00
Jonas Kvinge
87c2007352 Update Changelog 2019-01-09 21:32:32 +01:00
Jonas Kvinge
d53de00e2a Change default tab to FLAC 2019-01-09 20:11:30 +01:00
Jonas Kvinge
513aafca16 Disable fading when alsa device is selected 2019-01-09 20:02:40 +01:00
Jonas Kvinge
1d35923f0c Also disable shortcut options when x11 is disabled 2019-01-09 19:22:38 +01:00
Jonas Kvinge
4de61e79fb Fix background image 2019-01-09 18:24:45 +01:00
Jonas Kvinge
b2fe64ed34 Add warning if missing headers 2019-01-08 23:49:35 +01:00
Jonas Kvinge
411cb27ead Fix gnome d-dus shortcuts backend 2019-01-08 23:25:50 +01:00
Jonas Kvinge
696dc0051e Fix loading shortcuts settings 2019-01-08 23:25:01 +01:00
Jonas Kvinge
c0e1a9a359 Use VERSION_GREATER instead 2019-01-07 19:25:11 +01:00
Jonas Kvinge
b73f90c44d Try to fix build with older cmake 2019-01-07 19:10:41 +01:00
Jonas Kvinge
aeb7e3914e Allow compile with Qt 5.5 again 2019-01-07 19:04:53 +01:00
Jonas Kvinge
89e0524f90 Attempt to fix cmake error 2019-01-07 18:02:50 +01:00
Jonas Kvinge
bbc26a78f8 Add 3rdparty/taglib/README.md 2019-01-07 02:14:51 +01:00
Jonas Kvinge
74fb44abd3 Change to VERSION_GREATER_EQUAL 2019-01-07 02:12:32 +01:00
Jonas Kvinge
d692e4e5c6 Update README.md 2019-01-07 01:49:53 +01:00
Jonas Kvinge
5cf088c0de Add 3rdparty/README.md 2019-01-07 01:47:07 +01:00
Jonas Kvinge
f222048efe Replace QtSingleApplication with SingleApplication (#40) 2019-01-07 01:00:58 +01:00
Jonas Kvinge
21b2a694f0 Update Changelog 2019-01-06 21:39:23 +01:00
Jonas Kvinge
8e210d8e01 Fix this properly 2019-01-06 17:41:56 +01:00
Jonas Kvinge
7e9668584b Change default for collection to group by album artist 2019-01-06 17:26:05 +01:00
Jonas Kvinge
ffd16e5401 Enable organise without gstreamer 2019-01-06 16:48:23 +01:00
Jonas Kvinge
14cfd1a34e Re-enable transcoder and organiser, add transcoder for wavpack 2019-01-06 14:34:50 +01:00
Jonas Kvinge
a11f43520e Add support for Monkey's Audio 2019-01-06 00:32:58 +01:00
Jonas Kvinge
e4159dd7c2 Update Changelog 2019-01-06 00:32:32 +01:00
Jonas Kvinge
8738fa4137 Add create-dmg.sh to gitignore 2019-01-05 23:18:14 +01:00
Jonas Kvinge
9b7da3e071 Attempt to fix crashes in devices 2019-01-05 23:17:20 +01:00
Jonas Kvinge
a06de35aa5 Fix crashes in devices 2019-01-05 23:11:16 +01:00
Jonas Kvinge
d5fffc3641 Change outfile 2019-01-05 13:07:41 +01:00
Jonas Kvinge
a734ed8dfe Only upload file for master branch 2019-01-05 01:50:17 +01:00
Jonas Kvinge
9e12f8e3eb Fix permissions on ~/.ssh/id_rsa 2019-01-05 01:03:31 +01:00
Jonas Kvinge
600f297472 Fix git commands 2019-01-05 01:03:31 +01:00
Jonas Kvinge
92ee972e58 Fix rsync command 2019-01-05 01:03:31 +01:00
Jonas Kvinge
95a5c8a76a Fix macOS versioning 2019-01-05 01:03:31 +01:00
Jonas Kvinge
212d8e9278 Update .travis.yml 2019-01-05 01:03:31 +01:00
Jonas Kvinge
7f844cae63 Update .travis.yml 2019-01-04 01:23:25 +01:00
Jonas Kvinge
5034c92be9 Update .travis.yml 2019-01-04 01:23:06 +01:00
Jonas Kvinge
9378bac10e Add make dmg 2019-01-04 01:22:32 +01:00
Jonas Kvinge
b6db96b653 Fix macOS install 2019-01-04 01:13:03 +01:00
Jonas Kvinge
c271e05abc Use Q_OS_MACOS instead of Q_OS_DARWIN 2019-01-04 00:39:22 +01:00
Jonas Kvinge
de7154eda8 Deploy gst-plugin-scanner 2019-01-04 00:35:10 +01:00
Jonas Kvinge
030106986b Fix Utilities::SetEnv 2019-01-03 23:18:39 +01:00
Jonas Kvinge
32d33f03d7 Fix script 2019-01-03 23:18:39 +01:00
Jonas Kvinge
750a5e1147 Update .travis.yml 2019-01-03 23:18:39 +01:00
Jonas Kvinge
8dfcb353cb Update .travis.yml 2019-01-03 23:18:39 +01:00
Jonas Kvinge
9a2740dede Update .travis.yml 2019-01-03 23:18:39 +01:00
Jonas Kvinge
6d6874db37 +x 2019-01-03 23:18:39 +01:00
Jonas Kvinge
01c6b266e5 Update .travis.yml 2019-01-03 23:18:39 +01:00
Jonas Kvinge
d1445cb47e Update .travis.yml 2019-01-03 23:18:39 +01:00
Jonas Kvinge
5c44742a9b Add upload script 2019-01-03 23:18:39 +01:00
Jonas Kvinge
486eedc03e Update .travis.yml 2019-01-03 23:18:39 +01:00
Jonas Kvinge
7d8ba57e5f Fix bundle dir for macOS 2019-01-03 23:18:39 +01:00
Jonas Kvinge
ab827a91dc Remove prompt 2019-01-03 23:18:39 +01:00
Jonas Kvinge
e0bbcdb236 Update .travis.yml 2019-01-03 23:18:39 +01:00
Jonas Kvinge
3d4b6783ed Fix gst plugins 2019-01-03 23:18:39 +01:00
Jonas Kvinge
bb398b3275 Update .travis.yml 2019-01-03 23:18:39 +01:00
Jonas Kvinge
4d05bf034b Update .travis.yml 2019-01-03 23:18:39 +01:00
Jonas Kvinge
6594d90778 Change deployed plugins 2019-01-03 23:18:39 +01:00
Jonas Kvinge
ee3981b748 Fix dmg script 2019-01-03 23:18:39 +01:00
Jonas Kvinge
f57416d22b Update PKGBUILD 2019-01-03 02:49:08 +01:00
Jonas Kvinge
bcc103e572 Fix PKGBUILD 2019-01-03 02:44:52 +01:00
Jonas Kvinge
d398018633 Fix macos build 2019-01-02 00:32:36 +01:00
Jonas Kvinge
45c4be3ae9 Fix macos build 2019-01-01 22:35:23 +01:00
Jonas Kvinge
d93d72d538 Update Changelog 2019-01-01 22:22:31 +01:00
Jonas Kvinge
d3bcb38669 Fix macos build 2019-01-01 22:06:48 +01:00
Jonas Kvinge
7f19c43f4d Remove remaining qxt 2019-01-01 21:56:24 +01:00
Jonas Kvinge
98791015e5 Minor fixes to qtsingleapplication 2019-01-01 21:48:50 +01:00
Jonas Kvinge
213f08e171 Fix macos build 2019-01-01 21:40:36 +01:00
Jonas Kvinge
4641115314 Rename macos device finder 2019-01-01 20:22:19 +01:00
Jonas Kvinge
faee781042 Remove empty files 2019-01-01 20:15:01 +01:00
Jonas Kvinge
de4885d223 Fix globalshortcut status print 2019-01-01 20:11:46 +01:00
Jonas Kvinge
cef334c210 Add new global shortcut system backend for X11 and Windows
- Remove qxt
- Also create an option for enabled/disabling shortcuts through X11.
2019-01-01 20:07:29 +01:00
Jonas Kvinge
2a54cb17e7 Move organise files, add option to strip all non-fat characters 2018-12-29 15:37:16 +01:00
Jonas Kvinge
2e1b601508 Fix copyright header 2018-12-29 03:25:21 +01:00
Jonas Kvinge
719be46f9e Update Changelog 2018-12-29 03:21:40 +01:00
Jonas Kvinge
649c061ee1 Fix typo 2018-12-29 03:21:29 +01:00
Jonas Kvinge
1023a3da6f Fix some copyrights 2018-12-29 03:21:15 +01:00
Jonas Kvinge
404283be19 Convert devicemanager to QAbstractItemModel 2018-12-29 02:57:22 +01:00
Jonas Kvinge
fc9f93791d Remove Qt5::Xml dependency 2018-12-28 17:15:27 +01:00
Jonas Kvinge
55b0d1f24f Fix headers 2018-12-26 15:32:20 +01:00
Jonas Kvinge
d487351d5c Fix includes 2018-12-26 15:05:32 +01:00
Jonas Kvinge
e66fe78cbe Remove Disc from album name when scrobbling 2018-12-26 13:33:56 +01:00
Jonas Kvinge
ba38ccdbc6 Finish UpdateNowPlayingRequestFinished 2018-12-26 02:31:32 +01:00
Jonas Kvinge
a603e112e7 Fix error handling 2018-12-26 02:23:48 +01:00
Jonas Kvinge
99f154be70 Don't resubmit on single requests 2018-12-26 02:01:19 +01:00
Jonas Kvinge
1ea259f8e3 Do submit after successful login 2018-12-26 01:45:28 +01:00
Jonas Kvinge
8445f5587f Remove error output when no results are found 2018-12-26 01:28:25 +01:00
Jonas Kvinge
301ca055bd Fix submit and error handling 2018-12-26 01:17:17 +01:00
Jonas Kvinge
d9dc89f25c Fix scrobble status output for Last.fm 2018-12-25 23:58:55 +01:00
Jonas Kvinge
07cfdbbc25 Make Listenbrainz send playing now 2018-12-25 23:28:58 +01:00
Jonas Kvinge
b0c951cdd2 Fix initial submit value 2018-12-25 23:12:46 +01:00
Jonas Kvinge
bab01291b1 Make scrobble submit delay configurable 2018-12-25 22:58:34 +01:00
Jonas Kvinge
7c3f3da07d Fix min scrobble point 2018-12-25 22:58:11 +01:00
Jonas Kvinge
81d112928b Update descriptions 2018-12-24 01:11:47 +01:00
Jonas Kvinge
9c09f0ce82 Update message 2018-12-24 01:11:37 +01:00
Jonas Kvinge
a20a2dbb71 Ask before opening url, fix some typos 2018-12-24 00:15:53 +01:00
Jonas Kvinge
cbf262be71 Update Changelog 2018-12-23 19:29:54 +01:00
Jonas Kvinge
c58cfd407c Update Changelog 2018-12-23 19:28:52 +01:00
Jonas Kvinge
c5db43854b Update Changelog 2018-12-23 19:23:03 +01:00
Jonas Kvinge
52de8d0082 Fix key up causing playback to reset 2018-12-23 19:17:21 +01:00
Jonas Kvinge
dea810996f Change macOS spelling 2018-12-23 19:07:20 +01:00
Jonas Kvinge
f7af63c222 Add scrobbler icons 2018-12-23 19:04:54 +01:00
Jonas Kvinge
0d7e12e781 Add scrobbler with support for Last.fm, Libre.fm and ListenBrainz 2018-12-23 18:54:27 +01:00
Jonas Kvinge
517285085a Fix compile without gstreamer 2018-12-20 18:11:22 +01:00
Jonas Kvinge
ceb0f5ead4 Add new musicbrainz album cover provider 2018-12-15 15:12:18 +01:00
Jonas Kvinge
df5573f29a Merge branch 'master' of github.com:jonaski/strawberry 2018-12-15 02:18:28 +01:00
Jonas Kvinge
ce4e1b1ae4 Fix uninitialized variable 2018-12-15 02:16:47 +01:00
Jonas Kvinge
2114cc98bb Update Dockerfile 2018-12-15 02:13:39 +01:00
Jonas Kvinge
e38973ed4f Update Dockerfile 2018-12-15 01:09:14 +01:00
Jonas Kvinge
4f8aaa98f7 Update README.md 2018-12-15 00:56:25 +01:00
Jonas Kvinge
4148a310a2 Update debian copyright 2018-12-15 00:53:11 +01:00
Jonas Kvinge
35397f6a3e Fix includes 2018-12-15 00:52:58 +01:00
Jonas Kvinge
19168ec6e8 Remove remaining amazon cover provider files 2018-12-15 00:50:53 +01:00
Jonas Kvinge
2d087dfe15 Add new last.fm album cover provider 2018-12-15 00:43:50 +01:00
Jonas Kvinge
3226633f51 Add missing empty check 2018-12-15 00:43:00 +01:00
Jonas Kvinge
33d2e0f836 Replace 0 with nullptr 2018-12-15 00:41:22 +01:00
Jonas Kvinge
6ccf661678 Change windows setup filename 2018-12-13 23:43:15 +01:00
Jonas Kvinge
b5ba78def2 Add PKGBUILD to exclude 2018-12-13 23:31:13 +01:00
Jonas Kvinge
1706ba5765 Improve discogs cover provider 2018-12-02 23:29:22 +01:00
Jonas Kvinge
a8f13a4157 Remove sha2 from debian copyright 2018-12-02 19:18:26 +01:00
Jonas Kvinge
2d8f30c40a Replace sha2 with QCryptographicHash 2018-12-02 19:12:27 +01:00
Jonas Kvinge
d97d0fe359 Update debian copyright 2018-12-02 16:26:19 +01:00
Jonas Kvinge
1bae736284 Add debian copyright 2018-12-02 16:13:58 +01:00
Jonas Kvinge
017352b91e Add contributors to about 2018-12-02 16:12:10 +01:00
Jonas Kvinge
355afacaa1 Update URLs to https 2018-12-02 16:11:51 +01:00
Jonas Kvinge
fae79800c8 Fix spelling 2018-12-02 14:00:53 +01:00
Jonas Kvinge
db298ba5e2 Add new faded strawberry background image 2018-12-01 21:12:58 +01:00
Jonas Kvinge
06a20d43ce Add new faded strawberry background image 2018-12-01 20:52:16 +01:00
Thomas PIERSON
63f29235e4 Update and improve the manual pages. (#33) 2018-12-01 15:38:03 +01:00
Jonas Kvinge
9bb00d99f6 Turn back git revision 2018-11-29 19:51:11 +01:00
Jonas Kvinge
bcd29b9fd2 Release 0.4.2 2018-11-28 21:53:38 +01:00
Jonas Kvinge
046c221bc6 Change internet to streaming in settings and change order of settings 2018-11-28 17:53:37 +01:00
Jonas Kvinge
29a39b4e7f Attempt to fix devices issue 2018-11-28 17:45:50 +01:00
Jonas Kvinge
9fb3d06aac Change if statement in qtsingleapplication/CMakeLists.txt 2018-11-28 17:44:45 +01:00
Jonas Kvinge
142d5e2347 Update .travis.yml 2018-11-27 19:36:22 +01:00
Jonas Kvinge
58ea0bcbb0 Turn Deezer OFF by default if dependencies arent found 2018-11-27 19:33:30 +01:00
Jonas Kvinge
1c0bbe5d33 Fix endif in qxt CMakeLists.txt 2018-11-27 19:27:09 +01:00
Jonas Kvinge
8a7bba8ef3 Remove background image
Unsure about copyright issues using it, the image is from the Strawbs boxset, removing
it for now.
2018-11-27 19:20:33 +01:00
Jonas Kvinge
3b93963688 Code cleanup in qxtglobalshortcut 2018-11-27 19:14:35 +01:00
Jonas Kvinge
77e5d0288f Fix typo 2018-11-19 01:38:55 +01:00
Jonas Kvinge
460e78a1b8 Merge branch 'master' of github.com:jonaski/strawberry 2018-11-19 01:32:26 +01:00
Jonas Kvinge
fcbade267d Update changelog and about 2018-11-19 01:31:52 +01:00
Jonas Kvinge
41b99d9aa0 Update README.md 2018-11-19 01:12:32 +01:00
Jonas Kvinge
96c761dbb5 Update README.md 2018-11-19 01:09:51 +01:00
Jonas Kvinge
d297564236 Update README.md 2018-11-19 01:09:12 +01:00
Jonas Kvinge
bca1a98938 Add option to reset playlist columns 2018-11-19 00:18:48 +01:00
Jonas Kvinge
23205bef65 Playlist fixes
- Fix bug resetting playlist view columns to show all when using more than one
playlist.
- Add queue to play next
2018-11-18 23:21:12 +01:00
Jonas Kvinge
7613b2f526 Add alsa to optional_component 2018-11-17 16:28:05 +01:00
Jonas Kvinge
c5a521af1f Fix logging 2018-11-17 03:27:46 +01:00
Jonas Kvinge
f15c85e807 Remove SetThreadIOPriority, fixes poor performance on macos 2018-11-17 03:25:42 +01:00
Jonas Kvinge
6129ad1f4d Track slider fixes 2018-11-17 03:25:21 +01:00
Jonas Kvinge
9972dc192b Add include for QtGlobal 2018-11-17 03:24:20 +01:00
Jonas Kvinge
32b96a25e8 Remove dead code 2018-11-17 03:23:49 +01:00
Jonas Kvinge
9d09e7f6fe Remove dead code 2018-11-17 03:23:22 +01:00
Jonas Kvinge
5927483402 Remove qstyleoption_cast 2018-11-16 17:45:57 +01:00
Jonas Kvinge
f228f79a8a Fix macos code 2018-11-16 17:25:39 +01:00
Jonas Kvinge
072a3065cd Don't set paths for gst scanner and plugins on macos 2018-11-15 20:20:30 +01:00
Jonas Kvinge
718bd4c081 Replace NULL and 0 with nullptr 2018-11-14 00:43:19 +01:00
Jonas Kvinge
542cc0ec2f Remove unused code 2018-11-14 00:31:04 +01:00
Jonas Kvinge
b357634f51 Add pacman package files 2018-11-11 03:37:14 +01:00
Jonas Kvinge
6968c4d0fb Remove OpenGL from Dockerfile 2018-11-09 22:24:55 +01:00
Jonas Kvinge
e35d618133 Make Deezer engine use quality setting 2018-11-09 19:27:36 +01:00
Jonas Kvinge
4a23fde6bf Merge branch 'master' of github.com:jonaski/strawberry 2018-11-05 02:12:24 +01:00
Jonas Kvinge
1ae577641d Remove Qt5OpenGL from spec files and control file 2018-11-05 02:11:26 +01:00
Jonas Kvinge
686a3bb42b Update README.md 2018-11-05 01:21:37 +01:00
Jonas Kvinge
1771d41871 Fixes to makefiles
- Add a warning when using the systems taglib less or equal to version 1.11.1
- Remove OpenGL and Qt5::OpenGL dependency, it is not used by the analyzer
- Rename some variables
2018-11-05 01:14:56 +01:00
Jonas Kvinge
ee8f4ca7ab Add libqt5sql5-sqlite to debian control 2018-11-04 23:24:49 +01:00
Jonas Kvinge
4197a508a3 Remove obsolete xine warning and engine reinitialization 2018-11-04 21:23:34 +01:00
Jonas Kvinge
f38ffb505d Add Requires libQt5Sql5-sqlite to spec 2018-11-04 00:48:39 +01:00
Jonas Kvinge
679caf73d1 Fix strawberry.appdata.xml 2018-11-03 16:30:53 +01:00
Jonas Kvinge
56d25c346c Update appdata file to new AppStream specification 2018-11-03 15:57:45 +01:00
Jonas Kvinge
6ba26ba289 Change fedora rpm suffix 2018-11-02 21:30:07 +01:00
Jonas Kvinge
27582b7a4e Add strawberry.appdata.xml to spec files 2018-11-02 20:30:12 +01:00
Jonas Kvinge
2f4417d683 Fix files to delete on uninstall in nsi 2018-11-01 22:22:39 +01:00
Jonas Kvinge
de97d29a82 Turn back git revision 2018-11-01 22:22:30 +01:00
Jonas Kvinge
b782a2c8a2 Release 0.4.1 2018-11-01 21:18:57 +01:00
Jonas Kvinge
298783e2c5 Attempt to login if streamurl fails 2018-11-01 21:18:09 +01:00
Jonas Kvinge
bc9ec8025c Move some settings 2018-10-31 19:44:24 +01:00
Jonas Kvinge
26459763e5 Only set QtDebugMsg when build type is debug 2018-10-31 19:41:52 +01:00
Jonas Kvinge
1213306657 Update libcdio 2018-10-31 01:30:15 +01:00
Jonas Kvinge
50add12e4b Update changelog 2018-10-30 23:47:51 +01:00
Jonas Kvinge
67c889f982 Improve Xine engine code 2018-10-30 23:40:41 +01:00
Jonas Kvinge
08cba25071 Improve VLC error handling 2018-10-30 23:40:05 +01:00
Jonas Kvinge
ff35b01bac Improve gst engine error handling 2018-10-30 23:39:08 +01:00
Jonas Kvinge
faee1977fe Add better error handling between engine and player 2018-10-30 23:21:51 +01:00
Jonas Kvinge
6105b99a7f Add option to continue to next song in playlist on error
Also rename some settings. Add option to grey out songs both on playback and
on startup.
2018-10-30 23:20:02 +01:00
Jonas Kvinge
4993201b70 Fix deezer url handler 2018-10-30 23:18:25 +01:00
Jonas Kvinge
f456ca674d Update taglib 2018-10-27 12:15:52 +02:00
Jonas Kvinge
2ba350f76f Attempt to fix crash in setStyleSheet() 2018-10-26 20:05:37 +02:00
Jonas Kvinge
3fe92a62ac Add DSF and DSDIFF to IsFileLossless 2018-10-24 00:58:03 +02:00
Jonas Kvinge
f4dcf6821f Rename class InternetModel to InternetServices 2018-10-23 23:25:02 +02:00
Jonas Kvinge
0c10013858 Ops 2018-10-23 21:14:58 +02:00
Jonas Kvinge
3e9530fa8d Set metadata from deezer engine 2018-10-23 20:21:33 +02:00
Jonas Kvinge
0f4ffe4fce Add PCM as filetype 2018-10-23 20:21:01 +02:00
Jonas Kvinge
e1c01e5d25 Fix device selection 2018-10-23 20:18:07 +02:00
Jonas Kvinge
51021131d7 Allow compile on Qt 5.5.1 or above 2018-10-23 20:17:29 +02:00
Jonas Kvinge
89a08d1d5e Fix RPM build 2018-10-22 23:23:25 +02:00
Jonas Kvinge
ba8717f95a Fix Deezer engine error state 2018-10-22 23:04:34 +02:00
Jonas Kvinge
c8dfb9b0db Fix loading custom device 2018-10-22 22:10:27 +02:00
Jonas Kvinge
3a02ece169 Add EnsureInitialised() 2018-10-22 20:40:02 +02:00
Jonas Kvinge
262df6b461 Merge pull request #24 from eclipseo/use_qt5_deps
Use qt5 libraries of system-wide libraries
2018-10-22 19:00:07 +02:00
Jonas Kvinge
b9516fdfd4 Merge pull request #23 from eclipseo/add_appdata_file
Add AppStream data file
2018-10-22 18:49:28 +02:00
Robert-André Mauchin
9de41bc027 Use qt5 version of system-wide libraries 2018-10-22 17:45:47 +02:00
Robert-André Mauchin
53b5cf3855 Add AppStream data file 2018-10-22 16:44:35 +02:00
Jonas Kvinge
0567358783 Update README.md 2018-10-21 20:49:35 +02:00
Jonas Kvinge
c97bc55c6f Move queuemanager to fancytabbar 2018-10-21 15:13:48 +02:00
Jonas Kvinge
3ef0bf60d0 Correct text for right click configure 2018-10-20 22:33:23 +02:00
Jonas Kvinge
b7b8084a38 Remove setExpanding 2018-10-20 22:18:56 +02:00
Jonas Kvinge
5221f13498 Minor code cleanup and fixes 2018-10-20 22:16:22 +02:00
Jonas Kvinge
0c0a8d70f3 Change darwin to macos 2018-10-20 22:15:32 +02:00
Jonas Kvinge
2ff971878d Enable debug logging on Fedora 2018-10-20 22:13:51 +02:00
Jonas Kvinge
1a0dc1e614 Update README 2018-10-20 22:13:21 +02:00
Jonas Kvinge
4a0bd99654 Enable deezer engine by default 2018-10-20 22:13:02 +02:00
Jonas Kvinge
5cb98e44cb Update README 2018-10-19 20:42:58 +02:00
Jonas Kvinge
13ed99b9c3 Update README and Changelog 2018-10-19 20:37:42 +02:00
Jonas Kvinge
0cda4e27aa Replace qSort/qStableSort/qSwap 2018-10-19 20:18:46 +02:00
Jonas Kvinge
0969e7f504 Remove cachedlist.h 2018-10-19 19:38:30 +02:00
Jonas Kvinge
cad73e18e2 Remove unused code 2018-10-19 19:20:29 +02:00
Jonas Kvinge
9eadeddfd9 Fixup and finish deezer engine 2018-10-19 19:15:33 +02:00
Jonas Kvinge
de11cb173b Use previews as default setting if deezer engine and dzmedia is missing 2018-10-19 19:14:49 +02:00
Jonas Kvinge
5853bc68d1 Replace depreciated macro 2018-10-19 19:13:40 +02:00
Jonas Kvinge
7d1fd9d46f Add missing names 2018-10-19 19:13:24 +02:00
Jonas Kvinge
c977c822d5 Add info about Deezer 2018-10-19 19:13:01 +02:00
Jonas Kvinge
c05fb33ea2 Replace FancyTabWidget with improved version 2018-10-19 19:10:22 +02:00
Jonas Kvinge
b9d0b3e152 Add option to diable Tidal and Deezer 2018-10-17 23:49:02 +02:00
Jonas Kvinge
a9e905b301 Fix bug not loading engine 2018-10-17 22:55:36 +02:00
Jonas Kvinge
a8a714c820 Use common classes for Tidal and Deezer 2018-10-17 21:18:39 +02:00
Jonas Kvinge
9349ad9383 Fix missing icon 2018-10-17 00:46:08 +02:00
Jonas Kvinge
f686f00951 Fix missing icon 2018-10-17 00:43:05 +02:00
Jonas Kvinge
3d13c12cb7 Update taglib 2018-10-16 23:58:26 +02:00
Jonas Kvinge
2384a42d33 Update .gitignore 2018-10-16 21:37:27 +02:00
Jonas Kvinge
f507fec905 Update Changelog 2018-10-16 21:32:03 +02:00
Jonas Kvinge
83a9724d17 Improve Tidal code 2018-10-16 21:31:28 +02:00
Jonas Kvinge
4156e26f76 Fix some minor code bugs in deezer 2018-10-16 21:30:27 +02:00
Jonas Kvinge
6d269e1786 Add libdeezer dll to windows nsi 2018-10-16 21:29:40 +02:00
Jonas Kvinge
ea447cab37 Remove quotes 2018-10-16 21:29:21 +02:00
Jonas Kvinge
4f3e7de441 Exclude debian changelog 2018-10-16 21:29:00 +02:00
Jonas Kvinge
0a81fa99fc Add Deezer support 2018-10-14 00:08:33 +02:00
Jonas Kvinge
4aad44cb62 Create timer for login attempts for Tidal 2018-10-13 00:30:52 +02:00
Jonas Kvinge
69dda39d02 Create timer for login attempts for Tidal 2018-10-13 00:18:38 +02:00
Jonas Kvinge
ca3ba6f136 Increase kTrackSliderUpdateTimeMs 2018-10-02 01:01:31 +02:00
Jonas Kvinge
044cf4624a Add hide() 2018-10-02 00:58:46 +02:00
Jonas Kvinge
1fbfabdf66 Remove whitespaces 2018-10-02 00:46:54 +02:00
Jonas Kvinge
db035351be Remove whitespaces 2018-10-02 00:38:52 +02:00
Jonas Kvinge
2883ef840e Fix track stop on error 2018-10-02 00:21:50 +02:00
Jonas Kvinge
8254ee911d Fix default setting for system tray 2018-10-01 00:31:02 +02:00
Jonas Kvinge
560bc0a150 Fix default setting for system tray 2018-10-01 00:29:06 +02:00
Jonas Kvinge
827898cd38 Fix taglib includes 2018-09-30 22:32:56 +02:00
Jonas Kvinge
184dec146c Move resume playback outside of startup group 2018-09-30 15:55:02 +02:00
Jonas Kvinge
298dbe96c8 Check if system has system tray using QSystemTrayIcon::isSystemTrayAvailable() 2018-09-30 15:33:27 +02:00
Jonas Kvinge
6d888eb51a Analyzer code cleanup and try to fix crash on Fedora 2018-09-30 15:32:21 +02:00
Jonas Kvinge
3694765611 Add error handling for enabling FTS3 2018-09-30 14:54:14 +02:00
Jonas Kvinge
b07ae3d34e Remove reset 2018-09-30 00:08:09 +02:00
Jonas Kvinge
64bcdf4734 Fix spelling 2018-09-30 00:07:59 +02:00
Jonas Kvinge
1c23756fc4 Turn on git revision 2018-09-30 00:07:05 +02:00
Jonas Kvinge
50e3eeaafd Release 0.3.3 2018-09-24 19:13:45 +02:00
Jonas Kvinge
843f528ebc Revert change to Tidal login using clientUniqueKey 2018-09-24 19:10:49 +02:00
Jonas Kvinge
faa0076988 Turn back git revision 2018-09-24 19:10:15 +02:00
Jonas Kvinge
59622c52ad Release 0.3.2 2018-09-24 18:42:21 +02:00
Jonas Kvinge
a8e9aba58b Save password as QString 2018-09-24 18:40:05 +02:00
Jonas Kvinge
36563cd1e1 Fix compile 2018-09-22 23:32:11 +02:00
Jonas Kvinge
072e712f0d Add missing QUrl include 2018-09-22 23:30:19 +02:00
Jonas Kvinge
121a186160 Improve contextview and engine code 2018-09-22 23:13:56 +02:00
Jonas Kvinge
15a2ccc21e Change size of comboxes in device settings 2018-09-22 15:54:38 +02:00
Jonas Kvinge
dbd8ea69eb Turn off debug about in Tidal 2018-09-22 15:39:24 +02:00
Jonas Kvinge
8a57356f64 More Tidal fixes 2018-09-22 15:37:42 +02:00
Jonas Kvinge
c77cb002f3 Add support for both ALSA hw and plughw 2018-09-21 23:29:00 +02:00
Jonas Kvinge
aa83a2b40b Add option to change url stream scheme for Tidal 2018-09-21 18:53:27 +02:00
Jonas Kvinge
65b04cac6e Update changelog 2018-09-21 01:20:16 +02:00
Jonas Kvinge
5e577190a8 Fix some copyrights 2018-09-21 01:12:21 +02:00
Jonas Kvinge
0143617056 Fix bug setting wrong temp metadata and bug in pipeline 2018-09-21 00:34:02 +02:00
Jonas Kvinge
a77dde7d3b Fix some includes 2018-09-20 22:48:52 +02:00
Jonas Kvinge
7dcdb7c673 Might be a good idea to actually add the URL Handler :) 2018-09-20 22:37:38 +02:00
Jonas Kvinge
6de8eb56cd Remove dead code in Tidal 2018-09-20 22:29:35 +02:00
Jonas Kvinge
04e272d9bc Fix mistake 2018-09-20 22:22:48 +02:00
Jonas Kvinge
17fe201473 Use URL Handler for Tidal 2018-09-20 22:13:30 +02:00
Jonas Kvinge
25249be37f Change orange to red 2018-09-20 17:44:31 +02:00
Jonas Kvinge
97ec12b5b3 Don't compile devices on windows 2018-09-20 17:36:23 +02:00
Jonas Kvinge
246f82bfad Replace obsolete QStyleOptionViewItemV2-4 2018-09-18 22:17:28 +02:00
Jonas Kvinge
db5679bbe9 Fix lsb-release command 2018-09-18 19:01:59 +02:00
Jonas Kvinge
feb0e1c45b Fix Fedora RPM suffix 2018-09-18 18:43:04 +02:00
Jonas Kvinge
edba837295 Fix RPM Suffix 2018-09-18 17:28:49 +02:00
Jonas Kvinge
55882360ef Fix RPM suffix 2018-09-18 00:45:33 +02:00
Jonas Kvinge
6039370ad6 Fix exclude 2018-09-18 00:45:07 +02:00
Jonas Kvinge
2d238c08d3 Change output filename 2018-09-18 00:31:21 +02:00
Jonas Kvinge
9b337b6a34 Add missing nsi 2018-09-17 19:30:23 +02:00
Jonas Kvinge
370db791aa Add nsi files for x86_64-w64-mingw32 2018-09-17 01:02:48 +02:00
Jonas Kvinge
9e3c547580 Turn back on git revision 2018-09-16 23:13:30 +02:00
Jonas Kvinge
ab5d9b62b8 Fix search error not shown in Tidal search 2018-09-15 22:34:44 +02:00
Jonas Kvinge
8e35e0c476 Release 0.3.1 2018-09-15 19:22:55 +02:00
Jonas Kvinge
9d9492b528 Update README.md 2018-09-15 13:46:29 +02:00
Jonas Kvinge
9732cc2ce2 Update README.md 2018-09-15 13:45:26 +02:00
Jonas Kvinge
a4beea3f34 Update README.md 2018-09-15 13:43:33 +02:00
Jonas Kvinge
c9256c52bf Check for QT version 2018-09-15 13:35:03 +02:00
Jonas Kvinge
6563bec7e4 Remove builtin qtwin and use WinExtras, replace some macros 2018-09-15 13:07:51 +02:00
Jonas Kvinge
072facdf7b Update features 2018-09-15 00:22:10 +02:00
Jonas Kvinge
a0f07a7a67 Update about 2018-09-14 23:58:58 +02:00
Jonas Kvinge
1a7465ba94 Formatting and remove unused icons 2018-09-14 23:05:58 +02:00
Jonas Kvinge
be3862ec40 Add missing signal 2018-09-12 22:55:53 +02:00
Jonas Kvinge
a5276a3b7e Merge branch 'master' of github.com:jonaski/strawberry 2018-09-12 22:52:09 +02:00
Jonas Kvinge
fc8a27f55b Fix some compiler warnings 2018-09-12 22:51:10 +02:00
Jonas Kvinge
87718bee24 Update .travis.yml 2018-09-12 22:15:19 +02:00
Jonas Kvinge
56f2e00990 Update .travis.yml 2018-09-12 22:14:37 +02:00
Jonas Kvinge
27e3448dda Merge branch 'master' of github.com:jonaski/strawberry 2018-09-12 21:52:52 +02:00
Jonas Kvinge
9404e40cad Fix song.h 2018-09-12 21:52:30 +02:00
Jonas Kvinge
982f399aff Update .travis.yml 2018-09-12 21:17:45 +02:00
Jonas Kvinge
6d373737db Update .travis.yml 2018-09-12 21:09:00 +02:00
Jonas Kvinge
7f9e196234 Update .travis.yml 2018-09-12 20:56:51 +02:00
Jonas Kvinge
95d03de160 Update .travis.yml 2018-09-12 20:12:34 +02:00
Jonas Kvinge
d43190a41f Update .travis.yml 2018-09-12 19:45:46 +02:00
Jonas Kvinge
b5c0529969 Update .travis.yml 2018-09-12 19:42:04 +02:00
Jonas Kvinge
621a5b1de8 Update Dockerfile 2018-09-12 19:32:13 +02:00
Jonas Kvinge
e30f269412 Update .travis.yml 2018-09-11 23:32:19 +02:00
Jonas Kvinge
494ac20cca Create Dockerfile 2018-09-11 23:13:58 +02:00
Jonas Kvinge
bf997a6d2e Don't show error when there was no match from API Seeds 2018-09-11 00:38:06 +02:00
Jonas Kvinge
bc0c0b7950 Extract error from Json data in API Seeds 2018-09-11 00:20:24 +02:00
Jonas Kvinge
f202cf6988 Don't use lyrics from AudD if title and artist don't match 2018-09-11 00:01:17 +02:00
Jonas Kvinge
e62fb964ff Improve lyrics providers 2018-09-10 23:46:01 +02:00
Jonas Kvinge
9014781336 Fix Tidal authentication 2018-09-10 22:22:00 +02:00
Jonas Kvinge
e479e7e113 Fix show album cover from Tidal 2018-09-10 21:58:57 +02:00
Jonas Kvinge
f9379961e9 Fix Tidal authentication 2018-09-10 21:23:50 +02:00
Jonas Kvinge
e5e6cbf6e4 Hide samplerate, bitdepth and bitrate when not set 2018-09-08 13:25:40 +02:00
Jonas Kvinge
6d686ee66a Add source to songs and playlist_items 2018-09-08 12:38:02 +02:00
Jonas Kvinge
19b645d731 Show filetype for Tidal 2018-09-06 22:34:18 +02:00
Jonas Kvinge
28c685dcd8 Ops I did it again... 2018-09-06 20:51:14 +02:00
Jonas Kvinge
0abeefd470 Fixed saving (APE) tags for WavPack files 2018-09-06 20:49:05 +02:00
Jonas Kvinge
1562585561 Added support for reading lyrics from tags
Also fixed tagreader crash when saving tags to MP3 files
2018-09-06 20:04:29 +02:00
Jonas Kvinge
0a64a2a394 Finish Tidal
- Add configurable settings
- Add progressbar
- Simplify code
2018-09-06 17:39:26 +02:00
Jonas Kvinge
440a223bfb Fix saving search_for_cover_auto 2018-09-06 17:37:50 +02:00
Jonas Kvinge
36b0a22c79 Fix saving above_status_bar 2018-09-06 17:37:03 +02:00
Jonas Kvinge
032022c246 Add "Show above status bar" as an option 2018-09-05 19:03:21 +02:00
Jonas Kvinge
9e4f3f9867 Fix qocoa mac build 2018-09-05 18:15:28 +02:00
Jonas Kvinge
1119c87b2f Fix clear search box 2018-09-04 22:55:38 +02:00
Jonas Kvinge
36ab26c49a Fix automatic album cover fetching
- Moved Album Choice Controller to mainwindow to use common for both
PlayingWidget and ContextView.
- Fixed a bug I created that caused fetching album covers in a loop
2018-09-04 21:43:44 +02:00
Jonas Kvinge
460cddb3dc Add ResetWarning() to make sure label is hidden 2018-09-04 20:52:47 +02:00
Jonas Kvinge
a5c2dfacf0 Don't use discogs for automatic album cover search 2018-09-04 20:51:47 +02:00
Jonas Kvinge
b9788ef4b5 Update .travis.yml 2018-09-02 23:49:28 +02:00
Jonas Kvinge
4a124e596e Update .travis.yml 2018-09-02 17:04:58 +02:00
Jonas Kvinge
994a400ac9 Update nsi files 2018-09-02 16:20:48 +02:00
Jonas Kvinge
d08e6a1dca Remove libmpcdec-5.dll 2018-09-02 16:00:48 +02:00
Jonas Kvinge
154136185b Remove libxine from nsi file 2018-09-02 12:56:31 +02:00
Jonas Kvinge
f86b10c71f Add bold to albums by label 2018-09-02 12:50:28 +02:00
Jonas Kvinge
0b3d84a65e Update changelog 2018-09-02 12:50:12 +02:00
Jonas Kvinge
ee78b6f2bb Add DSF and DSDIFF/DFF support 2018-09-02 01:40:14 +02:00
Jonas Kvinge
521d5cf4fa Add DSF and DSDIFF support to taglib 2018-09-02 01:39:02 +02:00
Jonas Kvinge
52cf4412c8 Update changelog 2018-09-01 22:22:12 +02:00
Jonas Kvinge
dcae67ac7b Change search limit 2018-09-01 22:21:45 +02:00
Jonas Kvinge
cd0ee282c6 Add dist/windows/strawberry-debug.nsi.in to cmake 2018-09-01 22:14:59 +02:00
Jonas Kvinge
9d7e5864de Merge branch 'master' of github.com:jonaski/strawberry 2018-09-01 22:00:44 +02:00
Jonas Kvinge
d426eb2e07 Add seperate nsi file for debug 2018-09-01 22:00:06 +02:00
Jonas Kvinge
06bed7feb7 Update README.md 2018-08-29 23:26:14 +02:00
Jonas Kvinge
adc38c4b12 Disable Amazon cover provider (API key revoked) 2018-08-29 22:27:26 +02:00
Jonas Kvinge
5af58c19ae Remove some unused variables 2018-08-29 22:17:23 +02:00
Jonas Kvinge
ac6cac8da1 New context with albums and lyrics +++ much more
* Added new lyrics provider with lyrics from AudD and API Seeds
* New improved context widget with albums and lyrics
* Fixed playing and context widget getting stuck in play mode when there was an error
* Changed icons for artists in collection, tidal and cover manager
* Removed "search" icon from "Search automatically" checkbox (right click) that looked ugly
* Removed some unused widgets from the src/widgets directory
* Fixed initial size of window and side panel
* Fixed saving window size correctly
2018-08-29 21:42:24 +02:00
Jonas Kvinge
3b30e66e87 Fix DB schema 2018-08-15 01:28:37 +02:00
Jonas Kvinge
41b0b1efd4 Remove broken xine fader until it's properly fixed 2018-08-12 15:05:34 +02:00
Jonas Kvinge
099d098dc7 Remove install log 2018-08-10 22:01:59 +02:00
Jonas Kvinge
1a55b7f1b2 Add DLL's required for Tidal to windows installer 2018-08-09 23:31:24 +02:00
Jonas Kvinge
f5ba8da3c7 Remove info frame from DeviceViewContainer
Was broken and caused yellow background on windows
2018-08-09 22:30:18 +02:00
Jonas Kvinge
08fcedd925 Rename some UI stuff 2018-08-09 21:49:18 +02:00
Jonas Kvinge
57f6f40e7d Fix missing icons and rework some UI stuff 2018-08-09 21:04:30 +02:00
Jonas Kvinge
4ca99dde43 Remove bak file 2018-08-09 18:41:43 +02:00
Jonas Kvinge
c518e42e18 Remove whitespace 2018-08-09 18:39:44 +02:00
Jonas Kvinge
0c329e6d83 Update README.md 2018-08-09 18:21:57 +02:00
Jonas Kvinge
820124f9e1 Add tidal support 2018-08-09 18:10:03 +02:00
Jonas Kvinge
26062bd07b Remove unneeded include 2018-07-28 00:16:56 +02:00
Jonas Kvinge
73029ab7a2 Remove dead analyzer code from VLC engine 2018-07-28 00:09:39 +02:00
Jonas Kvinge
c3e916d945 Add some missing e-mails in about 2018-07-20 02:28:42 +02:00
Jonas Kvinge
6ee9f9d1ad Add some missing names to about 2018-07-20 02:07:47 +02:00
Jonas Kvinge
b2c26eb4ed Make chromaprint optional 2018-07-16 07:23:37 +02:00
Jonas Kvinge
e181b47e3f Add 'make deb' 2018-07-16 02:57:57 +02:00
Jonas Kvinge
dc91f19f30 Just use PkgConfig to find dbus
- dbus was not found on debian based distros, because they had the library in a different location
- Not sure why I added the find package cmake file in the first place, will fix it and readd it if this causes problems
2018-07-16 01:20:29 +02:00
Jonas Kvinge
e260fc5057 Add check for qpa/qplatformnativeinterface.h in qxt 2018-07-16 01:18:58 +02:00
Jonas Kvinge
2368fff243 Fix mistake 2018-07-12 17:17:20 +02:00
Jonas Kvinge
70db0a33d1 Add debian package 2018-07-12 17:10:38 +02:00
Jonas Kvinge
56f47909f4 Fix Fedora build 2018-07-12 01:51:31 +02:00
Jonas Kvinge
e22b47a9d1 Revert git version off for Windows 2018-07-12 00:18:55 +02:00
Jonas Kvinge
e32ed02a24 Fix RPM build 2018-07-12 00:07:29 +02:00
Jonas Kvinge
eb5d72a940 Fix build with cmake with < 3.1 2018-07-11 17:23:44 +02:00
Jonas Kvinge
cbd809621a Update README.md 2018-07-08 19:50:16 +02:00
Jonas Kvinge
ad5d5d0ecd Correct Qt 5 dependencies 2018-07-06 21:56:13 +02:00
Jonas Kvinge
6bc948453c Update README.md 2018-07-05 19:47:18 +02:00
Jonas Kvinge
803590daa8 Fix Travis-CI build 2018-07-05 19:11:47 +02:00
Jonas Kvinge
c63399ee98 Update .travis.yml 2018-07-05 18:56:14 +02:00
Jonas Kvinge
f5ff95dca4 Update .travis.yml 2018-07-05 18:44:46 +02:00
Jonas Kvinge
998357c0bb Update .travis.yml 2018-07-05 18:33:50 +02:00
Jonas Kvinge
b43331bc3c Update .travis.yml 2018-07-05 18:17:36 +02:00
Jonas Kvinge
ecfeb17869 Update .travis.yml 2018-07-05 18:08:18 +02:00
Jonas Kvinge
9d2c9c732e Update .travis.yml 2018-07-05 18:02:24 +02:00
Jonas Kvinge
6478fadedc Update .travis.yml 2018-07-05 17:58:53 +02:00
Jonas Kvinge
a62eaa919d Update README.md 2018-07-05 17:56:25 +02:00
Jonas Kvinge
328a86288c Update .travis.yml 2018-07-05 17:52:32 +02:00
Jonas Kvinge
f1902a5b63 Update .travis.yml 2018-07-05 17:42:16 +02:00
Jonas Kvinge
693bade5a6 Update .travis.yml 2018-07-05 17:34:58 +02:00
Jonas Kvinge
ca2acb177a Create .travis.yml 2018-07-05 17:18:49 +02:00
Jonas Kvinge
670cd4e081 Remove unused variable 2018-07-05 03:28:32 +02:00
Jonas Kvinge
3a228c42bb Release 0.2.1 2018-07-05 02:55:51 +02:00
Jonas Kvinge
73fd87f8aa Update README.md 2018-07-04 14:25:36 +02:00
Jonas Kvinge
3adc1b55fa Update dist/windows/strawberry.nsi 2018-07-04 14:20:02 +02:00
Jonas Kvinge
0fd31f3632 Turn off git revision for windows build 2018-07-04 03:10:27 +02:00
Jonas Kvinge
55e0255fc2 Update windows/strawberry.nsi 2018-07-04 02:53:27 +02:00
Jonas Kvinge
8d23f6c422 Update dist/windows/strawberry.nsi 2018-07-04 01:57:22 +02:00
Jonas Kvinge
8aebfdc88c Fix dbus compile in osd widget 2018-07-04 00:55:09 +02:00
Jonas Kvinge
a554032823 Fix compile without dbus 2018-07-03 21:21:33 +02:00
Jonas Kvinge
c818ce19e9 Show engine switch warning only if more than 1 2018-07-03 20:36:38 +02:00
Jonas Kvinge
602f9759bb Fix dbus compile without X11 2018-07-03 20:32:28 +02:00
Jonas Kvinge
a00b8b2dca Use HAVE_DBUS instead of QT_DBUS_LIB 2018-07-03 19:48:08 +02:00
Jonas Kvinge
6dccc8857b Cleanup linking of QT components 2018-07-03 19:33:09 +02:00
Jonas Kvinge
b0f86a38ca Replace fromAscii() with fromLatin1() 2018-07-03 19:31:49 +02:00
Jonas Kvinge
1e4088b3e5 Make macdevicelister compile 2018-07-03 19:30:54 +02:00
Jonas Kvinge
3c5da3c8bd Update spec file 2018-07-03 18:55:25 +02:00
Jonas Kvinge
8e9bf2653a Update README.md 2018-07-03 18:24:34 +02:00
Jonas Kvinge
0d6f377664 Update README.md 2018-07-03 18:23:05 +02:00
Jonas Kvinge
d30a63f79b Fix variable 2018-07-03 17:56:13 +02:00
Jonas Kvinge
ab3569a285 More macos fixes 2018-07-03 17:51:52 +02:00
Jonas Kvinge
efdaf57f99 Bummer 2018-07-01 22:35:09 +02:00
Jonas Kvinge
ada7325a04 Fix macos build 2018-07-01 22:26:46 +02:00
Jonas Kvinge
c4e75dea65 Fix macos build 2018-07-01 22:24:23 +02:00
Jonas Kvinge
41ce19ab97 Correct spelling 2018-07-01 15:35:34 +02:00
Jonas Kvinge
3894419b14 More engine fixes 2018-07-01 15:30:53 +02:00
Jonas Kvinge
af03c05559 Make cmake fail if git revision cant be set. 2018-07-01 15:29:35 +02:00
Jonas Kvinge
04f1d296ea More engine fixes 2018-07-01 01:29:52 +02:00
Jonas Kvinge
67df8f2243 Hide warning groupbox in backend settings 2018-06-29 00:57:26 +02:00
Jonas Kvinge
980d8a65a1 Hide warning label in backend settings when not shown. 2018-06-29 00:33:30 +02:00
Jonas Kvinge
fc66e2e2c7 Fixes to xine engine 2018-06-28 23:12:39 +02:00
Jonas Kvinge
505c1feb42 Merge branch 'master' of github.com:jonaski/strawberry 2018-06-28 01:17:24 +02:00
Jonas Kvinge
985b91e5f4 Fix setting output/device for Xine and VLC backend
- Fixed setting output and device on Xine and VLC backend
- Fixed track slider for Xine, VLC and Phonon
- Improved backend settings to better support multiple backends
- Added group by samplerate and bitdepth in collection
- Fixed crash on exit when existing instance of the application is already runnung caused by NVIDIA driver
- Changed Q_OS_MAC to Q_OS_MACOS
2018-06-28 01:15:32 +02:00
Jonas Kvinge
ef7b95220c Update README.md 2018-06-21 01:50:14 +02:00
Jonas Kvinge
6978983dd3 Update README.md 2018-06-20 15:31:37 +02:00
Jonas Kvinge
008c39cd00 Add gstsink to devicefinder 2018-06-17 15:07:11 +02:00
Jonas Kvinge
416beb6b8e Use BOOST_SCOPE_EXIT 2018-06-12 00:26:08 +02:00
Jonas Kvinge
c4e64b591d Fix crash 2018-06-11 22:35:46 +02:00
806 changed files with 38472 additions and 19249 deletions

7
.gitignore vendored
View File

@@ -34,6 +34,13 @@ Thumbs.db
*.rc
/.qmake.cache
/.qmake.stash
*.spec
*.nsi
*.plist
maketarball.sh
dist/macos/create-dmg.sh
dist/debian/changelog
dist/pacman/PKGBUILD
# qtcreator generated files
*.pro.user*

54
.travis.yml Normal file
View File

@@ -0,0 +1,54 @@
sudo: required
language: C++
os:
- linux
- osx
services:
- docker
compiler:
- gcc
- 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 [[ "$TRAVIS_OS_NAME" == "linux" ]]; then
docker build -f Dockerfile -t strawberry-build .;
docker run --name build -itd strawberry-build /bin/bash;
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;
brew install libcdio libmtp libimobiledevice libplist;
export Qt5_DIR=/usr/local/opt/qt5/lib/cmake;
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
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;
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
branches:
except:
- # Do not build tags that we create when we upload to GitHub Releases
- /^(?i:continuous)$/

59
3rdparty/README.md vendored Normal file
View File

@@ -0,0 +1,59 @@
3rdparty libraries located in this directory
============================================
singleapplication
-----------------
This is a small static library used by Strawberry to prevent it from starting twice per user session.
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.
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
------
TagLib is a library for reading and editing the meta-data of several popular audio formats. It is also used
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
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
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
The current taglib in 3rdparty also has the following features:
- Audio file detection by content.
- DSF and DSDIFF support
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.
URL: http://utfcpp.sourceforge.net/

13
3rdparty/SPMediaKeyTap/CMakeLists.txt vendored Normal file
View File

@@ -0,0 +1,13 @@
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 Normal file
View File

@@ -0,0 +1,8 @@
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 Normal file
View File

@@ -0,0 +1,12 @@
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.

View File

@@ -0,0 +1,30 @@
#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

View File

@@ -0,0 +1,128 @@
#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

View File

@@ -0,0 +1,28 @@
// 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];

View File

@@ -0,0 +1,12 @@
@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];

View File

@@ -0,0 +1,38 @@
#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 Normal file
View File

@@ -0,0 +1,34 @@
#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 Normal file
View File

@@ -0,0 +1,300 @@
// 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

View File

@@ -0,0 +1,25 @@
-(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;
}
}
}

View File

@@ -24,6 +24,7 @@ else()
qprogressindicatorspinning_nonmac.cpp
)
set(RESOURCES
qsearchfield_nonmac.qrc
qprogressindicatorspinning_nonmac.qrc
)
qt5_add_resources(RESOURCES_SOURCES ${RESOURCES})
@@ -31,3 +32,4 @@ endif()
add_library(Qocoa STATIC ${SOURCES} ${MOC_SOURCES} ${RESOURCES_SOURCES})
target_link_libraries(Qocoa ${QT_LIBRARIES})

View File

@@ -1,17 +0,0 @@
SOURCES += main.cpp\
gallery.cpp \
HEADERS += gallery.h \
qocoa_mac.h \
qsearchfield.h \
qbutton.h \
qprogressindicatorspinning.h \
mac {
OBJECTIVE_SOURCES += qsearchfield_mac.mm qbutton_mac.mm qprogressindicatorspinning_mac.mm
LIBS += -framework Foundation -framework Appkit
QMAKE_CFLAGS += -mmacosx-version-min=10.6
} else {
SOURCES += qsearchfield_nonmac.cpp qbutton_nonmac.cpp qprogressindicatorspinning_nonmac.cpp
RESOURCES += qsearchfield_nonmac.qrc qprogressindicatorspinning_nonmac.qrc
}

View File

@@ -16,7 +16,9 @@ make
```
## Status
Qocoa classes are currently provided for NSButton, a spinning NSProgressIndicator and NSSearchField. There is a [TODO list](https://github.com/mikemcquaid/Qocoa/blob/master/TODO.md) for classes I hope to implement.
I'm not personally working on this any more but will accept pull-requests.
[![Build Status](https://travis-ci.org/MikeMcQuaid/Qocoa.svg?branch=master)](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).
@@ -28,7 +30,9 @@ For each class you want to use copy the [`qocoa_mac.h`](https://github.com/mikem
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).
The 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/).
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
![Qocoa Gallery](https://github.com/mikemcquaid/Qocoa/raw/master/gallery.png)

View File

@@ -1,70 +0,0 @@
#include "gallery.h"
#include <QVBoxLayout>
#include "qsearchfield.h"
#include "qbutton.h"
#include "qprogressindicatorspinning.h"
Gallery::Gallery(QWidget *parent) : QWidget(parent)
{
setWindowTitle("Qocoa Gallery");
QVBoxLayout *layout = new QVBoxLayout(this);
QSearchField *searchField = new QSearchField(this);
layout->addWidget(searchField);
QButton *roundedButton = new QButton(this, QButton::Rounded);
roundedButton->setText("Button");
layout->addWidget(roundedButton);
QButton *regularSquareButton = new QButton(this, QButton::RegularSquare);
regularSquareButton->setText("Button");
layout->addWidget(regularSquareButton);
QButton *disclosureButton = new QButton(this, QButton::Disclosure);
layout->addWidget(disclosureButton);
QButton *shadowlessSquareButton = new QButton(this, QButton::ShadowlessSquare);
shadowlessSquareButton->setText("Button");
layout->addWidget(shadowlessSquareButton);
QButton *circularButton = new QButton(this, QButton::Circular);
layout->addWidget(circularButton);
QButton *textureSquareButton = new QButton(this, QButton::TexturedSquare);
textureSquareButton->setText("Textured Button");
layout->addWidget(textureSquareButton);
QButton *helpButton = new QButton(this, QButton::HelpButton);
layout->addWidget(helpButton);
QButton *smallSquareButton = new QButton(this, QButton::SmallSquare);
smallSquareButton->setText("Gradient Button");
layout->addWidget(smallSquareButton);
QButton *texturedRoundedButton = new QButton(this, QButton::TexturedRounded);
texturedRoundedButton->setText("Round Textured");
layout->addWidget(texturedRoundedButton);
QButton *roundedRectangleButton = new QButton(this, QButton::RoundRect);
roundedRectangleButton->setText("Rounded Rect Button");
layout->addWidget(roundedRectangleButton);
QButton *recessedButton = new QButton(this, QButton::Recessed);
recessedButton->setText("Recessed Button");
layout->addWidget(recessedButton);
QButton *roundedDisclosureButton = new QButton(this, QButton::RoundedDisclosure);
layout->addWidget(roundedDisclosureButton);
#ifdef MAC_OS_X_VERSION_10_7
QButton *inlineButton = new QButton(this, QButton::Inline);
inlineButton->setText("Inline Button");
layout->addWidget(inlineButton);
#endif
QProgressIndicatorSpinning *progressIndicatorSpinning = new QProgressIndicatorSpinning(this);
progressIndicatorSpinning->animate();
layout->addWidget(progressIndicatorSpinning);
}

View File

@@ -1,14 +0,0 @@
#ifndef GALLERY_H
#define GALLERY_H
#include <QWidget>
class Gallery : public QWidget
{
Q_OBJECT
public:
explicit Gallery(QWidget *parent = 0);
};
#endif // WIDGET_H

Binary file not shown.

Before

Width:  |  Height:  |  Size: 41 KiB

View File

@@ -1,12 +0,0 @@
#include <QApplication>
#include "gallery.h"
int main(int argc, char *argv[])
{
QApplication application(argc, argv);
Gallery gallery;
gallery.show();
return application.exec();
}

View File

@@ -3,7 +3,6 @@
#include <QWidget>
#include <QPointer>
#include <QString>
class QButtonPrivate;
class QButton : public QWidget
@@ -24,7 +23,7 @@ public:
RoundRect = 12,
Recessed = 13,
RoundedDisclosure = 14,
#ifdef MAC_OS_X_VERSION_10_7
#ifdef __MAC_10_7
Inline = 15
#endif
};

View File

@@ -37,7 +37,9 @@ public:
switch(bezelStyle) {
case QButton::Disclosure:
case QButton::Circular:
#ifdef __MAC_10_7
case QButton::Inline:
#endif
case QButton::RoundedDisclosure:
case QButton::HelpButton:
[nsButton setTitle:@""];
@@ -55,7 +57,7 @@ public:
font = [NSFont fontWithName:@"Lucida Grande Bold" size:12];
break;
#ifdef MAC_OS_X_VERSION_10_7
#ifdef __MAC_10_7
case QButton::Inline:
font = [NSFont boldSystemFontOfSize:[NSFont systemFontSizeForControlSize:NSSmallControlSize]];
break;
@@ -112,7 +114,7 @@ public:
qButton->setFixedHeight(22);
qButton->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Fixed);
break;
#ifdef MAC_OS_X_VERSION_10_7
#ifdef __MAC_10_7
case QButton::Inline:
qButton->setMinimumWidth(10);
qButton->setFixedHeight(16);
@@ -130,7 +132,7 @@ public:
[nsButton setButtonType:NSMomentaryPushInButton];
}
[nsButton setBezelStyle:bezelStyle];
[nsButton setBezelStyle:(__bridge NSBezelStyle)bezelStyle];
}
void clicked()

View File

@@ -20,19 +20,12 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/
#include <QtGlobal>
#include <QObject>
#include <QWidget>
#include <QPointer>
#include <QString>
#include <QPixmap>
#include <QAbstractButton>
#include <QBoxLayout>
#include <QPushButton>
#include "qbutton.h"
#include <QToolBar>
#include <QToolButton>
#include "qbutton.h"
#include <QPushButton>
#include <QVBoxLayout>
class QButtonPrivate : public QObject
{

View File

@@ -20,10 +20,11 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/
#include <AppKit/NSImage.h>
#include <Foundation/NSString.h>
#include <QByteArray>
#include <QString>
#include <QBoxLayout>
#include <QtMacExtras>
#include <QMacCocoaViewContainer>
static inline NSString* fromQString(const QString &string)
@@ -42,11 +43,11 @@ static inline QString toQString(NSString *string)
static inline NSImage* fromQPixmap(const QPixmap &pixmap)
{
CGImageRef cgImage = pixmap.toMacCGImageRef();
CGImageRef cgImage = QtMac::toCGImageRef(pixmap);
return [[NSImage alloc] initWithCGImage:cgImage size:NSZeroSize];
}
static inline void setupLayout(void *cocoaView, QWidget *parent)
static inline void setupLayout(NSView *cocoaView, QWidget *parent)
{
parent->setAttribute(Qt::WA_NativeWindow);
QVBoxLayout *layout = new QVBoxLayout(parent);

View File

@@ -17,7 +17,8 @@ public:
Aqua = 12
};
explicit QProgressIndicatorSpinning(QWidget *parent, Thickness thickness = Default);
explicit QProgressIndicatorSpinning(QWidget *parent,
Thickness thickness = Default);
public slots:
void animate(bool animate = true);
private:

View File

@@ -20,17 +20,12 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/
#include <QtGlobal>
#include <QObject>
#include <QWidget>
#include <QBoxLayout>
#include <QLabel>
#include <QMovie>
#include <QPointer>
#include <QSize>
#include "qprogressindicatorspinning.h"
#include <QVBoxLayout>
#include <QMovie>
#include <QLabel>
class QProgressIndicatorSpinningPrivate : public QObject
{
public:

View File

@@ -2,44 +2,42 @@
#define QSEARCHFIELD_H
#include <QWidget>
#include <QObject>
#include <QPointer>
#include <QString>
#include <QEvent>
#include <QResizeEvent>
#include <QMenu>
class QSearchFieldPrivate;
class QSearchField : public QWidget
{
Q_OBJECT
public:
explicit QSearchField(QWidget *parent);
class QSearchField : public QWidget {
Q_OBJECT
QString text() const;
QString placeholderText() const;
void setFocus(Qt::FocusReason reason);
Q_PROPERTY(QString text READ text WRITE setText NOTIFY textChanged USER true);
Q_PROPERTY(QString placeholderText READ placeholderText WRITE setPlaceholderText);
public:
explicit QSearchField(QWidget *parent);
QString text() const;
QString placeholderText() const;
void setFocus(Qt::FocusReason);
public slots:
void setText(const QString &text);
void setPlaceholderText(const QString &text);
void clear();
void selectAll();
void setFocus();
void setText(const QString &text);
void setPlaceholderText(const QString &text);
void clear();
void selectAll();
void setFocus();
signals:
void textChanged(const QString &text);
void editingFinished();
void returnPressed();
void textChanged(const QString &text);
void editingFinished();
void returnPressed();
protected:
void resizeEvent(QResizeEvent*);
bool eventFilter(QObject*, QEvent*);
void resizeEvent(QResizeEvent*);
bool eventFilter(QObject*, QEvent*);
private:
friend class QSearchFieldPrivate;
QPointer <QSearchFieldPrivate> pimpl;
Q_PROPERTY(QString placeholderText READ placeholderText WRITE setPlaceholderText);
friend class QSearchFieldPrivate;
QPointer <QSearchFieldPrivate> pimpl;
};
#endif // QSEARCHFIELD_H

View File

@@ -29,60 +29,53 @@ THE SOFTWARE.
#import "AppKit/NSSearchField.h"
#include <QApplication>
#include <QClipboard>
#include <QKeyEvent>
#include <QClipboard>
class QSearchFieldPrivate : public QObject
{
class QSearchFieldPrivate : public QObject {
public:
QSearchFieldPrivate(QSearchField *qSearchField, NSSearchField *nsSearchField)
: QObject(qSearchField), qSearchField(qSearchField), nsSearchField(nsSearchField) {}
QSearchFieldPrivate(QSearchField *qSearchField, NSSearchField *nsSearchField)
: QObject(qSearchField), qSearchField(qSearchField), nsSearchField(nsSearchField) {}
void textDidChange(const QString &text)
{
if (qSearchField)
emit qSearchField->textChanged(text);
void textDidChange(const QString &text) {
if (qSearchField) emit qSearchField->textChanged(text);
}
void textDidEndEditing() {
if (qSearchField)
emit qSearchField->editingFinished();
}
void textDidEndEditing()
{
if (qSearchField)
emit qSearchField->editingFinished();
void returnPressed() {
if (qSearchField) {
emit qSearchField->returnPressed();
QKeyEvent* event = new QKeyEvent(QEvent::KeyPress, Qt::Key_Return, Qt::NoModifier);
QApplication::postEvent(qSearchField, event);
}
}
void returnPressed()
{
if (qSearchField) {
emit qSearchField->returnPressed();
QKeyEvent* event = new QKeyEvent(QEvent::KeyPress, Qt::Key_Return, Qt::NoModifier);
QApplication::postEvent(qSearchField, event);
}
void keyDownPressed() {
if (qSearchField) {
QKeyEvent* event = new QKeyEvent(QEvent::KeyPress, Qt::Key_Down, Qt::NoModifier);
QApplication::postEvent(qSearchField, event);
}
}
void keyDownPressed()
{
if (qSearchField) {
QKeyEvent* event = new QKeyEvent(QEvent::KeyPress, Qt::Key_Down, Qt::NoModifier);
QApplication::postEvent(qSearchField, event);
}
void keyUpPressed() {
if (qSearchField) {
QKeyEvent* event = new QKeyEvent(QEvent::KeyPress, Qt::Key_Up, Qt::NoModifier);
QApplication::postEvent(qSearchField, event);
}
}
void keyUpPressed()
{
if (qSearchField) {
QKeyEvent* event = new QKeyEvent(QEvent::KeyPress, Qt::Key_Up, Qt::NoModifier);
QApplication::postEvent(qSearchField, event);
}
}
QPointer<QSearchField> qSearchField;
NSSearchField *nsSearchField;
QPointer<QSearchField> qSearchField;
NSSearchField *nsSearchField;
};
@interface QSearchFieldDelegate : NSObject<NSTextFieldDelegate>
{
@interface QSearchFieldDelegate : NSObject<NSTextFieldDelegate> {
@public
QPointer<QSearchFieldPrivate> pimpl;
QPointer<QSearchFieldPrivate> pimpl;
}
-(void)controlTextDidChange:(NSNotification*)notification;
-(void)controlTextDidEndEditing:(NSNotification*)notification;
@@ -90,35 +83,33 @@ public:
@implementation QSearchFieldDelegate
-(void)controlTextDidChange:(NSNotification*)notification {
Q_ASSERT(pimpl);
if (pimpl)
pimpl->textDidChange(toQString([[notification object] stringValue]));
}
-(BOOL)control: (NSControl *)control textView:
(NSTextView *)textView doCommandBySelector:
(SEL)commandSelector {
Q_ASSERT(pimpl);
if (!pimpl) return NO;
if (commandSelector == @selector(moveDown:)) {
pimpl->keyDownPressed();
return YES;
} else if (commandSelector == @selector(moveUp:)) {
pimpl->keyUpPressed();
return YES;
}
return NO;
Q_ASSERT(pimpl);
if (pimpl) pimpl->textDidChange(toQString([[notification object] stringValue]));
}
-(void)controlTextDidEndEditing:(NSNotification*)notification {
// No Q_ASSERT here as it is called on destruction.
if (!pimpl) return;
Q_UNUSED(notification);
// No Q_ASSERT here as it is called on destruction.
if (!pimpl) return;
pimpl->textDidEndEditing();
if ([[[notification userInfo] objectForKey:@"NSTextMovement"] intValue] == NSReturnTextMovement)
pimpl->returnPressed();
}
pimpl->textDidEndEditing();
if ([[[notification userInfo] objectForKey:@"NSTextMovement"] intValue] == NSReturnTextMovement)
pimpl->returnPressed();
-(BOOL)control: (NSControl *)control textView:
(NSTextView *)textView doCommandBySelector:
(SEL)commandSelector {
Q_ASSERT(pimpl);
if (!pimpl) return NO;
if (commandSelector == @selector(moveDown:)) {
pimpl->keyDownPressed();
return YES;
}
else if (commandSelector == @selector(moveUp:)) {
pimpl->keyUpPressed();
return YES;
}
return NO;
}
@end
@@ -129,157 +120,109 @@ public:
@implementation QocoaSearchField
-(BOOL)performKeyEquivalent:(NSEvent*)event {
// First, check if we have the focus.
// If no, it probably means this event isn't for us.
NSResponder* firstResponder = [[NSApp keyWindow] firstResponder];
if ([firstResponder isKindOfClass:[NSText class]] && [(NSText*)firstResponder delegate] == self) {
// First, check if we have the focus.
// If no, it probably means this event isn't for us.
NSResponder* firstResponder = [[NSApp keyWindow] firstResponder];
if ([firstResponder isKindOfClass:[NSText class]] &&
[(NSText*)firstResponder delegate] == self) {
if ([event type] == NSKeyDown && [event modifierFlags] & NSCommandKeyMask)
{
QString keyString = toQString([event characters]);
if (keyString == "a") // Cmd+a
{
[self performSelector:@selector(selectText:)];
return YES;
}
else if (keyString == "c") // Cmd+c
{
[[self currentEditor] copy: nil];
return YES;
}
else if (keyString == "v") // Cmd+v
{
[[self currentEditor] paste: nil];
return YES;
}
else if (keyString == "x") // Cmd+x
{
[[self currentEditor] cut: nil];
return YES;
}
}
if ([event type] == NSEventTypeKeyDown && [event modifierFlags] & NSEventModifierFlagCommand) {
QString keyString = toQString([event characters]);
if (keyString == "a") // Cmd+a
{
[self performSelector:@selector(selectText:)];
return YES;
}
else if (keyString == "c") // Cmd+c
{
[[self currentEditor] copy: nil];
return YES;
}
else if (keyString == "v") // Cmd+v
{
[[self currentEditor] paste: nil];
return YES;
}
else if (keyString == "x") // Cmd+x
{
[[self currentEditor] cut: nil];
return YES;
}
}
}
return NO;
return NO;
}
@end
QSearchField::QSearchField(QWidget *parent) : QWidget(parent)
{
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
NSSearchField *search = [[QocoaSearchField alloc] init];
QSearchFieldDelegate *delegate = [[QSearchFieldDelegate alloc] init];
pimpl = delegate->pimpl = new QSearchFieldPrivate(this, search);
[search setDelegate:delegate];
setupLayout(search, this);
setFixedHeight(24);
setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed);
[search release];
[pool drain];
QSearchField::QSearchField(QWidget *parent) : QWidget(parent) {
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
NSSearchField *search = [[QocoaSearchField alloc] init];
QSearchFieldDelegate *delegate = [[QSearchFieldDelegate alloc] init];
pimpl = delegate->pimpl = new QSearchFieldPrivate(this, search);
[search setDelegate:delegate];
setupLayout(search, this);
setFixedHeight(24);
setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed);
[search release];
[pool drain];
}
void QSearchField::setText(const QString &text)
{
Q_ASSERT(pimpl);
if (!pimpl)
return;
void QSearchField::setText(const QString &text) {
Q_ASSERT(pimpl);
if (!pimpl) return;
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
[pimpl->nsSearchField setStringValue:fromQString(text)];
if (!text.isEmpty()) {
[pimpl->nsSearchField selectText:pimpl->nsSearchField];
[[pimpl->nsSearchField currentEditor] setSelectedRange:NSMakeRange([[pimpl->nsSearchField stringValue] length], 0)];
}
[pool drain];
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
[pimpl->nsSearchField setStringValue:fromQString(text)];
if (!text.isEmpty()) {
[pimpl->nsSearchField selectText:pimpl->nsSearchField];
[[pimpl->nsSearchField currentEditor] setSelectedRange:NSMakeRange([[pimpl->nsSearchField stringValue] length], 0)];
}
[pool drain];
}
void QSearchField::setPlaceholderText(const QString &text)
{
Q_ASSERT(pimpl);
if (!pimpl)
return;
void QSearchField::setPlaceholderText(const QString &text) {
Q_ASSERT(pimpl);
if (!pimpl) return;
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
[[pimpl->nsSearchField cell] setPlaceholderString:fromQString(text)];
[pool drain];
}
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
[[pimpl->nsSearchField cell] setPlaceholderString:fromQString(text)];
[pool drain];
void QSearchField::clear() {
Q_ASSERT(pimpl);
if (!pimpl) return;
[pimpl->nsSearchField setStringValue:@""];
emit textChanged(QString());
}
void QSearchField::selectAll() {
Q_ASSERT(pimpl);
if (!pimpl) return;
[pimpl->nsSearchField performSelector:@selector(selectText:)];
}
QString QSearchField::text() const {
Q_ASSERT(pimpl);
if (!pimpl) return QString();
return toQString([pimpl->nsSearchField stringValue]);
}
QString QSearchField::placeholderText() const {
Q_ASSERT(pimpl);
NSString* placeholder = [[pimpl->nsSearchField cell] placeholderString];
return toQString(placeholder);
Q_ASSERT(pimpl);
return toQString([[pimpl->nsSearchField cell] placeholderString]);
}
void QSearchField::setFocus(Qt::FocusReason reason)
{
/* Do nothing: we were previously using makeFirstResponder on search field, but
* that resulted in having the text being selected (and I didn't find any way to
* deselect it) which would result in the user erasing the first letter he just
* typed, after using setText (e.g. if the user typed a letter while having
* focus on the playlist, which means we call setText and give focus to the
* search bar).
* Instead now the focus will take place when calling selectText in setText.
* This obviously breaks the purpose of this function, but we never call only
* setFocus on a search box in Clementine (i.e. without a call to setText
* shortly after).
*/
void QSearchField::setFocus(Qt::FocusReason) {}
// Q_ASSERT(pimpl);
// if (!pimpl)
// return;
// if ([pimpl->nsSearchField acceptsFirstResponder]) {
// [[pimpl->nsSearchField window] makeFirstResponder: pimpl->nsSearchField];
// }
void QSearchField::setFocus() {
setFocus(Qt::OtherFocusReason);
}
void QSearchField::setFocus()
{
setFocus(Qt::OtherFocusReason);
void QSearchField::resizeEvent(QResizeEvent *resizeEvent) {
QWidget::resizeEvent(resizeEvent);
}
void QSearchField::clear()
{
Q_ASSERT(pimpl);
if (!pimpl)
return;
[pimpl->nsSearchField setStringValue:@""];
emit textChanged(QString());
bool QSearchField::eventFilter(QObject *o, QEvent *e) {
return QWidget::eventFilter(o, e);
}
void QSearchField::selectAll()
{
Q_ASSERT(pimpl);
if (!pimpl)
return;
[pimpl->nsSearchField performSelector:@selector(selectText:)];
}
QString QSearchField::text() const
{
Q_ASSERT(pimpl);
if (!pimpl)
return QString();
return toQString([pimpl->nsSearchField stringValue]);
}
void QSearchField::resizeEvent(QResizeEvent *resizeEvent)
{
QWidget::resizeEvent(resizeEvent);
}
bool QSearchField::eventFilter(QObject *o, QEvent *e)
{
return QWidget::eventFilter(o, e);
}

View File

@@ -20,169 +20,142 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/
#include <QtGlobal>
#include <QObject>
#include <QWidget>
#include <QApplication>
#include <QPointer>
#include <QString>
#include <QIcon>
#include <QSize>
#include <QStyle>
#include <QLineEdit>
#include <QBoxLayout>
#include <QToolButton>
#include <QtEvents>
#include "../../src/core/iconloader.h"
#include "qsearchfield.h"
class QSearchFieldPrivate : public QObject
{
#include <QApplication>
#include <QDesktopWidget>
#include <QLineEdit>
#include <QVBoxLayout>
#include <QToolButton>
#include <QStyle>
#include <QEvent>
#include <QDir>
#include <QDebug>
#include "../../src/core/iconloader.h"
class QSearchFieldPrivate : public QObject {
public:
QSearchFieldPrivate(QSearchField *searchField, QLineEdit *lineEdit, QToolButton *clearButton)
: QObject(searchField), lineEdit(lineEdit), clearButton(clearButton) {}
int lineEditFrameWidth() const {
return lineEdit->style()->pixelMetric(QStyle::PM_DefaultFrameWidth);
}
int clearButtonPaddedWidth() const {
return clearButton->width() + lineEditFrameWidth() * 2;
}
int clearButtonPaddedHeight() const {
return clearButton->height() + lineEditFrameWidth() * 2;
}
QPointer<QLineEdit> lineEdit;
QPointer<QToolButton> clearButton;
QSearchFieldPrivate(QSearchField *searchField, QLineEdit *lineEdit, QToolButton *clearButton)
: QObject(searchField), lineEdit(lineEdit), clearButton(clearButton) {}
int lineEditFrameWidth() const {
return lineEdit->style()->pixelMetric(QStyle::PM_DefaultFrameWidth);
}
int clearButtonPaddedWidth() const {
return clearButton->width() + lineEditFrameWidth() * 2;
}
int clearButtonPaddedHeight() const {
return clearButton->height() + lineEditFrameWidth() * 2;
}
QPointer<QLineEdit> lineEdit;
QPointer<QToolButton> clearButton;
};
QSearchField::QSearchField(QWidget *parent) : QWidget(parent)
{
QLineEdit *lineEdit = new QLineEdit(this);
connect(lineEdit, SIGNAL(textChanged(QString)),
this, SIGNAL(textChanged(QString)));
connect(lineEdit, SIGNAL(editingFinished()),
this, SIGNAL(editingFinished()));
connect(lineEdit, SIGNAL(returnPressed()),
this, SIGNAL(returnPressed()));
connect(lineEdit, SIGNAL(textChanged(QString)),
this, SLOT(setText(QString)));
QSearchField::QSearchField(QWidget *parent) : QWidget(parent) {
QIcon clearIcon(IconLoader::Load("edit-clear-locationbar-ltr"));
QLineEdit *lineEdit = new QLineEdit(this);
connect(lineEdit, SIGNAL(textChanged(QString)), this, SIGNAL(textChanged(QString)));
connect(lineEdit, SIGNAL(editingFinished()), this, SIGNAL(editingFinished()));
connect(lineEdit, SIGNAL(returnPressed()), this, SIGNAL(returnPressed()));
connect(lineEdit, SIGNAL(textChanged(QString)), this, SLOT(setText(QString)));
QToolButton *clearButton = new QToolButton(this);
clearButton->setIcon(clearIcon);
clearButton->setIconSize(QSize(16, 16));
clearButton->setStyleSheet("border: none; padding: 0px;");
clearButton->resize(clearButton->sizeHint());
connect(clearButton, SIGNAL(clicked()), this, SLOT(clear()));
QToolButton *clearButton = new QToolButton(this);
QIcon clearIcon(IconLoader::Load("edit-clear-locationbar-ltr"));
pimpl = new QSearchFieldPrivate(this, lineEdit, clearButton);
clearButton->setIcon(clearIcon);
clearButton->setIconSize(QSize(16, 16));
clearButton->setStyleSheet("border: none; padding: 0px;");
clearButton->resize(clearButton->sizeHint());
const int frame_width = lineEdit->style()->pixelMetric(QStyle::PM_DefaultFrameWidth);
connect(clearButton, SIGNAL(clicked()), this, SLOT(clear()));
lineEdit->setStyleSheet(QString("QLineEdit { padding-left: %1px; } ").arg(clearButton->width()));
const int width = frame_width + qMax(lineEdit->minimumSizeHint().width(), pimpl->clearButtonPaddedWidth());
const int height = frame_width + qMax(lineEdit->minimumSizeHint().height(), pimpl->clearButtonPaddedHeight());
lineEdit->setMinimumSize(width, height);
pimpl = new QSearchFieldPrivate(this, lineEdit, clearButton);
QVBoxLayout *layout = new QVBoxLayout(this);
layout->setMargin(0);
layout->addWidget(lineEdit);
const int frame_width = lineEdit->style()->pixelMetric(QStyle::PM_DefaultFrameWidth);
lineEdit->setStyleSheet(QString("QLineEdit { padding-left: %1px; } ").arg(clearButton->width()));
const int width = frame_width + qMax(lineEdit->minimumSizeHint().width(), pimpl->clearButtonPaddedWidth());
const int height = frame_width + qMax(lineEdit->minimumSizeHint().height(), pimpl->clearButtonPaddedHeight());
lineEdit->setMinimumSize(width, height);
QVBoxLayout *layout = new QVBoxLayout(this);
layout->setMargin(0);
layout->addWidget(lineEdit);
lineEdit->installEventFilter(this);
lineEdit->installEventFilter(this);
}
void QSearchField::setText(const QString &text)
{
Q_ASSERT(pimpl && pimpl->clearButton && pimpl->lineEdit);
if (!(pimpl && pimpl->clearButton && pimpl->lineEdit))
return;
void QSearchField::setText(const QString &text) {
Q_ASSERT(pimpl && pimpl->clearButton && pimpl->lineEdit);
if (!(pimpl && pimpl->clearButton && pimpl->lineEdit)) return;
if (text != this->text()) pimpl->lineEdit->setText(text);
if (text != this->text())
pimpl->lineEdit->setText(text);
}
void QSearchField::setPlaceholderText(const QString &text)
{
Q_ASSERT(pimpl && pimpl->lineEdit);
if (!(pimpl && pimpl->lineEdit))
return;
#if QT_VERSION >= 0x040700
pimpl->lineEdit->setPlaceholderText(text);
#endif
void QSearchField::setPlaceholderText(const QString &text) {
Q_ASSERT(pimpl && pimpl->lineEdit);
if (!(pimpl && pimpl->lineEdit)) return;
pimpl->lineEdit->setPlaceholderText(text);
}
QString QSearchField::placeholderText() const {
#if QT_VERSION >= 0x040700
return pimpl->lineEdit->placeholderText();
#else
return QString();
#endif
}
void QSearchField::setFocus(Qt::FocusReason reason)
{
Q_ASSERT(pimpl && pimpl->lineEdit);
if (pimpl && pimpl->lineEdit)
pimpl->lineEdit->setFocus(reason);
void QSearchField::setFocus(Qt::FocusReason reason) {
Q_ASSERT(pimpl && pimpl->lineEdit);
if (pimpl && pimpl->lineEdit) pimpl->lineEdit->setFocus(reason);
}
void QSearchField::setFocus()
{
setFocus(Qt::OtherFocusReason);
void QSearchField::setFocus() {
setFocus(Qt::OtherFocusReason);
}
void QSearchField::clear()
{
Q_ASSERT(pimpl && pimpl->lineEdit);
if (!(pimpl && pimpl->lineEdit))
return;
pimpl->lineEdit->clear();
void QSearchField::clear() {
Q_ASSERT(pimpl && pimpl->lineEdit);
if (!(pimpl && pimpl->lineEdit)) return;
pimpl->lineEdit->clear();
}
void QSearchField::selectAll()
{
Q_ASSERT(pimpl && pimpl->lineEdit);
if (!(pimpl && pimpl->lineEdit))
return;
pimpl->lineEdit->selectAll();
void QSearchField::selectAll() {
Q_ASSERT(pimpl && pimpl->lineEdit);
if (!(pimpl && pimpl->lineEdit)) return;
pimpl->lineEdit->selectAll();
}
QString QSearchField::text() const
{
Q_ASSERT(pimpl && pimpl->lineEdit);
if (!(pimpl && pimpl->lineEdit))
return QString();
return pimpl->lineEdit->text();
QString QSearchField::text() const {
Q_ASSERT(pimpl && pimpl->lineEdit);
if (!(pimpl && pimpl->lineEdit)) return QString();
return pimpl->lineEdit->text();
}
void QSearchField::resizeEvent(QResizeEvent *resizeEvent)
{
Q_ASSERT(pimpl && pimpl->clearButton && pimpl->lineEdit);
if (!(pimpl && pimpl->clearButton && pimpl->lineEdit))
return;
void QSearchField::resizeEvent(QResizeEvent *resizeEvent) {
Q_ASSERT(pimpl && pimpl->clearButton && pimpl->lineEdit);
if (!(pimpl && pimpl->clearButton && pimpl->lineEdit)) return;
QWidget::resizeEvent(resizeEvent);
const int x = pimpl->lineEditFrameWidth();
const int y = (height() - pimpl->clearButton->height())/2;
pimpl->clearButton->move(x, y);
QWidget::resizeEvent(resizeEvent);
const int x = pimpl->lineEditFrameWidth();
const int y = (height() - pimpl->clearButton->height())/2;
pimpl->clearButton->move(x, y);
}
bool QSearchField::eventFilter(QObject *o, QEvent *e)
{
if (pimpl && pimpl->lineEdit && o == pimpl->lineEdit) {
// Forward some lineEdit events to QSearchField (only those we need for
// now, but some might be added later if needed)
switch (e->type()) {
case QEvent::FocusIn:
case QEvent::FocusOut:
QApplication::sendEvent(this, e);
break;
}
bool QSearchField::eventFilter(QObject *o, QEvent *e) {
if (pimpl && pimpl->lineEdit && o == pimpl->lineEdit) {
// Forward some lineEdit events to QSearchField (only those we need for
// now, but some might be added later if needed)
switch (e->type()) {
case QEvent::FocusIn:
case QEvent::FocusOut:
QApplication::sendEvent(this, e);
break;
}
return QWidget::eventFilter(o, e);
}
return QWidget::eventFilter(o, e);
}

View File

@@ -0,0 +1,7 @@
<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>

Binary file not shown.

After

Width:  |  Height:  |  Size: 736 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 300 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 439 B

View File

@@ -1,29 +0,0 @@
cmake_minimum_required(VERSION 2.8.11)
set(CMAKE_CXX_STANDARD 11)
set(SINGLEAPP-SOURCES
qtlocalpeer.cpp
qtsingleapplication.cpp
qtsinglecoreapplication.cpp
)
set(SINGLEAPP-MOC-HEADERS
qtlocalpeer.h
qtsingleapplication.h
qtsinglecoreapplication.h
)
if(WIN32)
set(SINGLEAPP-SOURCES ${SINGLEAPP-SOURCES} qtlockedfile_win.cpp)
elseif(WIN32)
set(SINGLEAPP-SOURCES ${SINGLEAPP-SOURCES} qtlockedfile_unix.cpp)
endif(WIN32)
QT5_WRAP_CPP(SINGLEAPP-SOURCES-MOC ${SINGLEAPP-MOC-HEADERS})
ADD_LIBRARY(qtsingleapplication STATIC
${SINGLEAPP-SOURCES}
${SINGLEAPP-SOURCES-MOC}
)
target_link_libraries(qtsingleapplication Qt5::Core Qt5::Widgets Qt5::Network)

View File

@@ -1,504 +0,0 @@
GNU LESSER GENERAL PUBLIC LICENSE
Version 2.1, February 1999
Copyright (C) 1991, 1999 Free Software Foundation, Inc.
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
[This is the first released version of the Lesser GPL. It also counts
as the successor of the GNU Library Public License, version 2, hence
the version number 2.1.]
Preamble
The licenses for most software are designed to take away your
freedom to share and change it. By contrast, the GNU General Public
Licenses are intended to guarantee your freedom to share and change
free software--to make sure the software is free for all its users.
This license, the Lesser General Public License, applies to some
specially designated software packages--typically libraries--of the
Free Software Foundation and other authors who decide to use it. You
can use it too, but we suggest you first think carefully about whether
this license or the ordinary General Public License is the better
strategy to use in any particular case, based on the explanations below.
When we speak of free software, we are referring to freedom of use,
not price. Our General Public Licenses are designed to make sure that
you have the freedom to distribute copies of free software (and charge
for this service if you wish); that you receive source code or can get
it if you want it; that you can change the software and use pieces of
it in new free programs; and that you are informed that you can do
these things.
To protect your rights, we need to make restrictions that forbid
distributors to deny you these rights or to ask you to surrender these
rights. These restrictions translate to certain responsibilities for
you if you distribute copies of the library or if you modify it.
For example, if you distribute copies of the library, whether gratis
or for a fee, you must give the recipients all the rights that we gave
you. You must make sure that they, too, receive or can get the source
code. If you link other code with the library, you must provide
complete object files to the recipients, so that they can relink them
with the library after making changes to the library and recompiling
it. And you must show them these terms so they know their rights.
We protect your rights with a two-step method: (1) we copyright the
library, and (2) we offer you this license, which gives you legal
permission to copy, distribute and/or modify the library.
To protect each distributor, we want to make it very clear that
there is no warranty for the free library. Also, if the library is
modified by someone else and passed on, the recipients should know
that what they have is not the original version, so that the original
author's reputation will not be affected by problems that might be
introduced by others.
Finally, software patents pose a constant threat to the existence of
any free program. We wish to make sure that a company cannot
effectively restrict the users of a free program by obtaining a
restrictive license from a patent holder. Therefore, we insist that
any patent license obtained for a version of the library must be
consistent with the full freedom of use specified in this license.
Most GNU software, including some libraries, is covered by the
ordinary GNU General Public License. This license, the GNU Lesser
General Public License, applies to certain designated libraries, and
is quite different from the ordinary General Public License. We use
this license for certain libraries in order to permit linking those
libraries into non-free programs.
When a program is linked with a library, whether statically or using
a shared library, the combination of the two is legally speaking a
combined work, a derivative of the original library. The ordinary
General Public License therefore permits such linking only if the
entire combination fits its criteria of freedom. The Lesser General
Public License permits more lax criteria for linking other code with
the library.
We call this license the "Lesser" General Public License because it
does Less to protect the user's freedom than the ordinary General
Public License. It also provides other free software developers Less
of an advantage over competing non-free programs. These disadvantages
are the reason we use the ordinary General Public License for many
libraries. However, the Lesser license provides advantages in certain
special circumstances.
For example, on rare occasions, there may be a special need to
encourage the widest possible use of a certain library, so that it becomes
a de-facto standard. To achieve this, non-free programs must be
allowed to use the library. A more frequent case is that a free
library does the same job as widely used non-free libraries. In this
case, there is little to gain by limiting the free library to free
software only, so we use the Lesser General Public License.
In other cases, permission to use a particular library in non-free
programs enables a greater number of people to use a large body of
free software. For example, permission to use the GNU C Library in
non-free programs enables many more people to use the whole GNU
operating system, as well as its variant, the GNU/Linux operating
system.
Although the Lesser General Public License is Less protective of the
users' freedom, it does ensure that the user of a program that is
linked with the Library has the freedom and the wherewithal to run
that program using a modified version of the Library.
The precise terms and conditions for copying, distribution and
modification follow. Pay close attention to the difference between a
"work based on the library" and a "work that uses the library". The
former contains code derived from the library, whereas the latter must
be combined with the library in order to run.
GNU LESSER GENERAL PUBLIC LICENSE
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
0. This License Agreement applies to any software library or other
program which contains a notice placed by the copyright holder or
other authorized party saying it may be distributed under the terms of
this Lesser General Public License (also called "this License").
Each licensee is addressed as "you".
A "library" means a collection of software functions and/or data
prepared so as to be conveniently linked with application programs
(which use some of those functions and data) to form executables.
The "Library", below, refers to any such software library or work
which has been distributed under these terms. A "work based on the
Library" means either the Library or any derivative work under
copyright law: that is to say, a work containing the Library or a
portion of it, either verbatim or with modifications and/or translated
straightforwardly into another language. (Hereinafter, translation is
included without limitation in the term "modification".)
"Source code" for a work means the preferred form of the work for
making modifications to it. For a library, complete source code means
all the source code for all modules it contains, plus any associated
interface definition files, plus the scripts used to control compilation
and installation of the library.
Activities other than copying, distribution and modification are not
covered by this License; they are outside its scope. The act of
running a program using the Library is not restricted, and output from
such a program is covered only if its contents constitute a work based
on the Library (independent of the use of the Library in a tool for
writing it). Whether that is true depends on what the Library does
and what the program that uses the Library does.
1. You may copy and distribute verbatim copies of the Library's
complete source code as you receive it, in any medium, provided that
you conspicuously and appropriately publish on each copy an
appropriate copyright notice and disclaimer of warranty; keep intact
all the notices that refer to this License and to the absence of any
warranty; and distribute a copy of this License along with the
Library.
You may charge a fee for the physical act of transferring a copy,
and you may at your option offer warranty protection in exchange for a
fee.
2. You may modify your copy or copies of the Library or any portion
of it, thus forming a work based on the Library, and copy and
distribute such modifications or work under the terms of Section 1
above, provided that you also meet all of these conditions:
a) The modified work must itself be a software library.
b) You must cause the files modified to carry prominent notices
stating that you changed the files and the date of any change.
c) You must cause the whole of the work to be licensed at no
charge to all third parties under the terms of this License.
d) If a facility in the modified Library refers to a function or a
table of data to be supplied by an application program that uses
the facility, other than as an argument passed when the facility
is invoked, then you must make a good faith effort to ensure that,
in the event an application does not supply such function or
table, the facility still operates, and performs whatever part of
its purpose remains meaningful.
(For example, a function in a library to compute square roots has
a purpose that is entirely well-defined independent of the
application. Therefore, Subsection 2d requires that any
application-supplied function or table used by this function must
be optional: if the application does not supply it, the square
root function must still compute square roots.)
These requirements apply to the modified work as a whole. If
identifiable sections of that work are not derived from the Library,
and can be reasonably considered independent and separate works in
themselves, then this License, and its terms, do not apply to those
sections when you distribute them as separate works. But when you
distribute the same sections as part of a whole which is a work based
on the Library, the distribution of the whole must be on the terms of
this License, whose permissions for other licensees extend to the
entire whole, and thus to each and every part regardless of who wrote
it.
Thus, it is not the intent of this section to claim rights or contest
your rights to work written entirely by you; rather, the intent is to
exercise the right to control the distribution of derivative or
collective works based on the Library.
In addition, mere aggregation of another work not based on the Library
with the Library (or with a work based on the Library) on a volume of
a storage or distribution medium does not bring the other work under
the scope of this License.
3. You may opt to apply the terms of the ordinary GNU General Public
License instead of this License to a given copy of the Library. To do
this, you must alter all the notices that refer to this License, so
that they refer to the ordinary GNU General Public License, version 2,
instead of to this License. (If a newer version than version 2 of the
ordinary GNU General Public License has appeared, then you can specify
that version instead if you wish.) Do not make any other change in
these notices.
Once this change is made in a given copy, it is irreversible for
that copy, so the ordinary GNU General Public License applies to all
subsequent copies and derivative works made from that copy.
This option is useful when you wish to copy part of the code of
the Library into a program that is not a library.
4. You may copy and distribute the Library (or a portion or
derivative of it, under Section 2) in object code or executable form
under the terms of Sections 1 and 2 above provided that you accompany
it with the complete corresponding machine-readable source code, which
must be distributed under the terms of Sections 1 and 2 above on a
medium customarily used for software interchange.
If distribution of object code is made by offering access to copy
from a designated place, then offering equivalent access to copy the
source code from the same place satisfies the requirement to
distribute the source code, even though third parties are not
compelled to copy the source along with the object code.
5. A program that contains no derivative of any portion of the
Library, but is designed to work with the Library by being compiled or
linked with it, is called a "work that uses the Library". Such a
work, in isolation, is not a derivative work of the Library, and
therefore falls outside the scope of this License.
However, linking a "work that uses the Library" with the Library
creates an executable that is a derivative of the Library (because it
contains portions of the Library), rather than a "work that uses the
library". The executable is therefore covered by this License.
Section 6 states terms for distribution of such executables.
When a "work that uses the Library" uses material from a header file
that is part of the Library, the object code for the work may be a
derivative work of the Library even though the source code is not.
Whether this is true is especially significant if the work can be
linked without the Library, or if the work is itself a library. The
threshold for this to be true is not precisely defined by law.
If such an object file uses only numerical parameters, data
structure layouts and accessors, and small macros and small inline
functions (ten lines or less in length), then the use of the object
file is unrestricted, regardless of whether it is legally a derivative
work. (Executables containing this object code plus portions of the
Library will still fall under Section 6.)
Otherwise, if the work is a derivative of the Library, you may
distribute the object code for the work under the terms of Section 6.
Any executables containing that work also fall under Section 6,
whether or not they are linked directly with the Library itself.
6. As an exception to the Sections above, you may also combine or
link a "work that uses the Library" with the Library to produce a
work containing portions of the Library, and distribute that work
under terms of your choice, provided that the terms permit
modification of the work for the customer's own use and reverse
engineering for debugging such modifications.
You must give prominent notice with each copy of the work that the
Library is used in it and that the Library and its use are covered by
this License. You must supply a copy of this License. If the work
during execution displays copyright notices, you must include the
copyright notice for the Library among them, as well as a reference
directing the user to the copy of this License. Also, you must do one
of these things:
a) Accompany the work with the complete corresponding
machine-readable source code for the Library including whatever
changes were used in the work (which must be distributed under
Sections 1 and 2 above); and, if the work is an executable linked
with the Library, with the complete machine-readable "work that
uses the Library", as object code and/or source code, so that the
user can modify the Library and then relink to produce a modified
executable containing the modified Library. (It is understood
that the user who changes the contents of definitions files in the
Library will not necessarily be able to recompile the application
to use the modified definitions.)
b) Use a suitable shared library mechanism for linking with the
Library. A suitable mechanism is one that (1) uses at run time a
copy of the library already present on the user's computer system,
rather than copying library functions into the executable, and (2)
will operate properly with a modified version of the library, if
the user installs one, as long as the modified version is
interface-compatible with the version that the work was made with.
c) Accompany the work with a written offer, valid for at
least three years, to give the same user the materials
specified in Subsection 6a, above, for a charge no more
than the cost of performing this distribution.
d) If distribution of the work is made by offering access to copy
from a designated place, offer equivalent access to copy the above
specified materials from the same place.
e) Verify that the user has already received a copy of these
materials or that you have already sent this user a copy.
For an executable, the required form of the "work that uses the
Library" must include any data and utility programs needed for
reproducing the executable from it. However, as a special exception,
the materials to be distributed need not include anything that is
normally distributed (in either source or binary form) with the major
components (compiler, kernel, and so on) of the operating system on
which the executable runs, unless that component itself accompanies
the executable.
It may happen that this requirement contradicts the license
restrictions of other proprietary libraries that do not normally
accompany the operating system. Such a contradiction means you cannot
use both them and the Library together in an executable that you
distribute.
7. You may place library facilities that are a work based on the
Library side-by-side in a single library together with other library
facilities not covered by this License, and distribute such a combined
library, provided that the separate distribution of the work based on
the Library and of the other library facilities is otherwise
permitted, and provided that you do these two things:
a) Accompany the combined library with a copy of the same work
based on the Library, uncombined with any other library
facilities. This must be distributed under the terms of the
Sections above.
b) Give prominent notice with the combined library of the fact
that part of it is a work based on the Library, and explaining
where to find the accompanying uncombined form of the same work.
8. You may not copy, modify, sublicense, link with, or distribute
the Library except as expressly provided under this License. Any
attempt otherwise to copy, modify, sublicense, link with, or
distribute the Library is void, and will automatically terminate your
rights under this License. However, parties who have received copies,
or rights, from you under this License will not have their licenses
terminated so long as such parties remain in full compliance.
9. You are not required to accept this License, since you have not
signed it. However, nothing else grants you permission to modify or
distribute the Library or its derivative works. These actions are
prohibited by law if you do not accept this License. Therefore, by
modifying or distributing the Library (or any work based on the
Library), you indicate your acceptance of this License to do so, and
all its terms and conditions for copying, distributing or modifying
the Library or works based on it.
10. Each time you redistribute the Library (or any work based on the
Library), the recipient automatically receives a license from the
original licensor to copy, distribute, link with or modify the Library
subject to these terms and conditions. You may not impose any further
restrictions on the recipients' exercise of the rights granted herein.
You are not responsible for enforcing compliance by third parties with
this License.
11. If, as a consequence of a court judgment or allegation of patent
infringement or for any other reason (not limited to patent issues),
conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License. If you cannot
distribute so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you
may not distribute the Library at all. For example, if a patent
license would not permit royalty-free redistribution of the Library by
all those who receive copies directly or indirectly through you, then
the only way you could satisfy both it and this License would be to
refrain entirely from distribution of the Library.
If any portion of this section is held invalid or unenforceable under any
particular circumstance, the balance of the section is intended to apply,
and the section as a whole is intended to apply in other circumstances.
It is not the purpose of this section to induce you to infringe any
patents or other property right claims or to contest validity of any
such claims; this section has the sole purpose of protecting the
integrity of the free software distribution system which is
implemented by public license practices. Many people have made
generous contributions to the wide range of software distributed
through that system in reliance on consistent application of that
system; it is up to the author/donor to decide if he or she is willing
to distribute software through any other system and a licensee cannot
impose that choice.
This section is intended to make thoroughly clear what is believed to
be a consequence of the rest of this License.
12. If the distribution and/or use of the Library is restricted in
certain countries either by patents or by copyrighted interfaces, the
original copyright holder who places the Library under this License may add
an explicit geographical distribution limitation excluding those countries,
so that distribution is permitted only in or among countries not thus
excluded. In such case, this License incorporates the limitation as if
written in the body of this License.
13. The Free Software Foundation may publish revised and/or new
versions of the Lesser General Public License from time to time.
Such new versions will be similar in spirit to the present version,
but may differ in detail to address new problems or concerns.
Each version is given a distinguishing version number. If the Library
specifies a version number of this License which applies to it and
"any later version", you have the option of following the terms and
conditions either of that version or of any later version published by
the Free Software Foundation. If the Library does not specify a
license version number, you may choose any version ever published by
the Free Software Foundation.
14. If you wish to incorporate parts of the Library into other free
programs whose distribution conditions are incompatible with these,
write to the author to ask for permission. For software which is
copyrighted by the Free Software Foundation, write to the Free
Software Foundation; we sometimes make exceptions for this. Our
decision will be guided by the two goals of preserving the free status
of all derivatives of our free software and of promoting the sharing
and reuse of software generally.
NO WARRANTY
15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO
WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY
KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE
LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME
THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN
WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY
AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU
FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR
CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE
LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING
RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A
FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
DAMAGES.
END OF TERMS AND CONDITIONS
How to Apply These Terms to Your New Libraries
If you develop a new library, and you want it to be of the greatest
possible use to the public, we recommend making it free software that
everyone can redistribute and change. You can do so by permitting
redistribution under these terms (or, alternatively, under the terms of the
ordinary General Public License).
To apply these terms, attach the following notices to the library. It is
safest to attach them to the start of each source file to most effectively
convey the exclusion of warranty; and each file should have at least the
"copyright" line and a pointer to where the full notice is found.
<one line to give the library's name and a brief idea of what it does.>
Copyright (C) <year> <name of author>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
Also add information on how to contact you by electronic and paper mail.
You should also get your employer (if you work as a programmer) or your
school, if any, to sign a "copyright disclaimer" for the library, if
necessary. Here is a sample; alter the names:
Yoyodyne, Inc., hereby disclaims all copyright interest in the
library `Frob' (a library for tweaking knobs) written by James Random Hacker.
<signature of Ty Coon>, 1 April 1990
Ty Coon, President of Vice
That's all there is to it!

View File

@@ -1,210 +0,0 @@
/****************************************************************************
**
** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
** All rights reserved.
**
** Contact: Nokia Corporation (qt-info@nokia.com)
**
** This file is part of a Qt Solutions component.
**
** You may use this file under the terms of the BSD license as follows:
**
** "Redistribution and use in source and binary forms, with or without
** modification, are permitted provided that the following conditions are
** met:
** * Redistributions of source code must retain the above copyright
** notice, this list of conditions and the following disclaimer.
** * Redistributions in binary form must reproduce the above copyright
** notice, this list of conditions and the following disclaimer in
** the documentation and/or other materials provided with the
** distribution.
** * Neither the name of Nokia Corporation and its Subsidiary(-ies) 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
** OWNER 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."
**
****************************************************************************/
#include "qtlocalpeer.h"
#include <unistd.h>
#include <QtGlobal>
#include <QCoreApplication>
#include <QObject>
#include <QAbstractSocket>
#include <QByteArray>
#include <QChar>
#include <QDataStream>
#include <QDir>
#include <QFile>
#include <QIODevice>
#include <QLocalServer>
#include <QLocalSocket>
#include <QRegExp>
#if defined(Q_OS_WIN)
#include <qt_windows.h>
#include <QLibrary>
typedef BOOL(WINAPI*PProcessIdToSessionId)(DWORD,DWORD*);
static PProcessIdToSessionId pProcessIdToSessionId = 0;
#endif
#if defined(Q_OS_UNIX)
#include <time.h>
#endif
#include "qtlockedfile.cpp"
#if defined(Q_OS_WIN)
#include "qtlockedfile_win.cpp"
#else
#include "qtlockedfile_unix.cpp"
#endif
const char* QtLocalPeer::ack = "ack";
QtLocalPeer::QtLocalPeer(QObject* parent, const QString &appId)
: QObject(parent), id(appId)
{
QString prefix = id;
if (id.isEmpty()) {
id = QCoreApplication::applicationFilePath();
#if defined(Q_OS_WIN)
id = id.toLower();
#endif
prefix = id.section(QLatin1Char('/'), -1);
}
prefix.remove(QRegExp("[^a-zA-Z]"));
prefix.truncate(6);
QByteArray idc = id.toUtf8();
quint16 idNum = qChecksum(idc.constData(), idc.size());
socketName = QLatin1String("qtsingleapp-") + prefix + QLatin1Char('-') + QString::number(idNum, 16);
#if defined(Q_OS_WIN)
if (!pProcessIdToSessionId) {
QLibrary lib("kernel32");
pProcessIdToSessionId = (PProcessIdToSessionId)lib.resolve("ProcessIdToSessionId");
}
if (pProcessIdToSessionId) {
DWORD sessionId = 0;
pProcessIdToSessionId(GetCurrentProcessId(), &sessionId);
socketName += QLatin1Char('-') + QString::number(sessionId, 16);
}
#else
socketName += QLatin1Char('-') + QString::number(::getuid(), 16);
#endif
server = new QLocalServer(this);
QString lockName = QDir(QDir::tempPath()).absolutePath() + QLatin1Char('/') + socketName + QLatin1String("-lockfile");
lockFile.setFileName(lockName);
lockFile.open(QIODevice::ReadWrite);
}
bool QtLocalPeer::isClient()
{
if (lockFile.isLocked())
return false;
if (!lockFile.lock(QtLockedFile::WriteLock, false))
return true;
bool res = server->listen(socketName);
#if defined(Q_OS_UNIX) && (QT_VERSION >= QT_VERSION_CHECK(4,5,0))
// ### Workaround
if (!res && server->serverError() == QAbstractSocket::AddressInUseError) {
QFile::remove(QDir::cleanPath(QDir::tempPath())+QLatin1Char('/')+socketName);
res = server->listen(socketName);
}
#endif
if (!res)
qWarning("QtSingleCoreApplication: listen on local socket failed, %s", qPrintable(server->errorString()));
QObject::connect(server, SIGNAL(newConnection()), SLOT(receiveConnection()));
return false;
}
bool QtLocalPeer::sendMessage(const QString &message, int timeout)
{
if (!isClient())
return false;
QLocalSocket socket;
bool connOk = false;
for(int i = 0; i < 2; i++) {
// Try twice, in case the other instance is just starting up
socket.connectToServer(socketName);
connOk = socket.waitForConnected(timeout/2);
if (connOk || i)
break;
int ms = 250;
#if defined(Q_OS_WIN)
Sleep(DWORD(ms));
#else
struct timespec ts = { ms / 1000, (ms % 1000) * 1000 * 1000 };
nanosleep(&ts, NULL);
#endif
}
if (!connOk)
return false;
QByteArray uMsg(message.toUtf8());
QDataStream ds(&socket);
ds.writeBytes(uMsg.constData(), uMsg.size());
bool res = socket.waitForBytesWritten(timeout);
if (res) {
res &= socket.waitForReadyRead(timeout); // wait for ack
if (res)
res &= (socket.read(qstrlen(ack)) == ack);
}
return res;
}
void QtLocalPeer::receiveConnection()
{
QLocalSocket* socket = server->nextPendingConnection();
if (!socket)
return;
while (socket->bytesAvailable() < (int)sizeof(quint32))
socket->waitForReadyRead();
QDataStream ds(socket);
QByteArray uMsg;
quint32 remaining;
ds >> remaining;
uMsg.resize(remaining);
int got = 0;
char* uMsgBuf = uMsg.data();
do {
got = ds.readRawData(uMsgBuf, remaining);
remaining -= got;
uMsgBuf += got;
}
while (remaining && got >= 0 && socket->waitForReadyRead(2000));
if (got < 0) {
qWarning("QtLocalPeer: Message reception failed %s", socket->errorString().toLatin1().constData());
delete socket;
return;
}
QString message(QString::fromUtf8(uMsg));
socket->write(ack, qstrlen(ack));
socket->waitForBytesWritten(1000);
delete socket;
emit messageReceived(message); //### (might take a long time to return)
}

View File

@@ -1,76 +0,0 @@
/****************************************************************************
**
** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
** All rights reserved.
**
** Contact: Nokia Corporation (qt-info@nokia.com)
**
** This file is part of a Qt Solutions component.
**
** You may use this file under the terms of the BSD license as follows:
**
** "Redistribution and use in source and binary forms, with or without
** modification, are permitted provided that the following conditions are
** met:
** * Redistributions of source code must retain the above copyright
** notice, this list of conditions and the following disclaimer.
** * Redistributions in binary form must reproduce the above copyright
** notice, this list of conditions and the following disclaimer in
** the documentation and/or other materials provided with the
** distribution.
** * Neither the name of Nokia Corporation and its Subsidiary(-ies) 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
** OWNER 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."
**
****************************************************************************/
#ifndef QTLOCALPEER_H
#define QTLOCALPEER_H
#include <QObject>
#include <QString>
#include <QLocalServer>
#include "qtlockedfile.h"
class QtLocalPeer : public QObject
{
Q_OBJECT
public:
QtLocalPeer(QObject *parent = 0, const QString &appId = QString());
bool isClient();
bool sendMessage(const QString &message, int timeout);
QString applicationId() const
{ return id; }
Q_SIGNALS:
void messageReceived(const QString &message);
protected Q_SLOTS:
void receiveConnection();
protected:
QString id;
QString socketName;
QLocalServer *server;
QtLockedFile lockFile;
private:
static const char* ack;
};
#endif // QTLOCALPEER_H

View File

@@ -1,195 +0,0 @@
/****************************************************************************
**
** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
** All rights reserved.
**
** Contact: Nokia Corporation (qt-info@nokia.com)
**
** This file is part of a Qt Solutions component.
**
** You may use this file under the terms of the BSD license as follows:
**
** "Redistribution and use in source and binary forms, with or without
** modification, are permitted provided that the following conditions are
** met:
** * Redistributions of source code must retain the above copyright
** notice, this list of conditions and the following disclaimer.
** * Redistributions in binary form must reproduce the above copyright
** notice, this list of conditions and the following disclaimer in
** the documentation and/or other materials provided with the
** distribution.
** * Neither the name of Nokia Corporation and its Subsidiary(-ies) 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
** OWNER 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."
**
****************************************************************************/
#include <QFile>
#include <QString>
#include "qtlockedfile.h"
/*!
\class QtLockedFile
\brief The QtLockedFile class extends QFile with advisory locking
functions.
A file may be locked in read or write mode. Multiple instances of
\e QtLockedFile, created in multiple processes running on the same
machine, may have a file locked in read mode. Exactly one instance
may have it locked in write mode. A read and a write lock cannot
exist simultaneously on the same file.
The file locks are advisory. This means that nothing prevents
another process from manipulating a locked file using QFile or
file system functions offered by the OS. Serialization is only
guaranteed if all processes that access the file use
QLockedFile. Also, while holding a lock on a file, a process
must not open the same file again (through any API), or locks
can be unexpectedly lost.
The lock provided by an instance of \e QtLockedFile is released
whenever the program terminates. This is true even when the
program crashes and no destructors are called.
*/
/*! \enum QtLockedFile::LockMode
This enum describes the available lock modes.
\value ReadLock A read lock.
\value WriteLock A write lock.
\value NoLock Neither a read lock nor a write lock.
*/
/*!
Constructs an unlocked \e QtLockedFile object. This constructor
behaves in the same way as \e QFile::QFile().
\sa QFile::QFile()
*/
QtLockedFile::QtLockedFile()
: QFile()
{
#ifdef Q_OS_WIN
wmutex = 0;
rmutex = 0;
#endif
m_lock_mode = NoLock;
}
/*!
Constructs an unlocked QtLockedFile object with file \a name. This
constructor behaves in the same way as \e QFile::QFile(const
QString&).
\sa QFile::QFile()
*/
QtLockedFile::QtLockedFile(const QString &name)
: QFile(name)
{
#ifdef Q_OS_WIN
wmutex = 0;
rmutex = 0;
#endif
m_lock_mode = NoLock;
}
/*!
Opens the file in OpenMode \a mode.
This is identical to QFile::open(), with the one exception that the
Truncate mode flag is disallowed. Truncation would conflict with the
advisory file locking, since the file would be modified before the
write lock is obtained. If truncation is required, use resize(0)
after obtaining the write lock.
Returns true if successful; otherwise false.
\sa QFile::open(), QFile::resize()
*/
bool QtLockedFile::open(OpenMode mode)
{
if (mode & QIODevice::Truncate) {
qWarning("QtLockedFile::open(): Truncate mode not allowed.");
return false;
}
return QFile::open(mode);
}
/*!
Returns \e true if this object has a in read or write lock;
otherwise returns \e false.
\sa lockMode()
*/
bool QtLockedFile::isLocked() const
{
return m_lock_mode != NoLock;
}
/*!
Returns the type of lock currently held by this object, or \e
QtLockedFile::NoLock.
\sa isLocked()
*/
QtLockedFile::LockMode QtLockedFile::lockMode() const
{
return m_lock_mode;
}
/*!
\fn bool QtLockedFile::lock(LockMode mode, bool block = true)
Obtains a lock of type \a mode. The file must be opened before it
can be locked.
If \a block is true, this function will block until the lock is
aquired. If \a block is false, this function returns \e false
immediately if the lock cannot be aquired.
If this object already has a lock of type \a mode, this function
returns \e true immediately. If this object has a lock of a
different type than \a mode, the lock is first released and then a
new lock is obtained.
This function returns \e true if, after it executes, the file is
locked by this object, and \e false otherwise.
\sa unlock(), isLocked(), lockMode()
*/
/*!
\fn bool QtLockedFile::unlock()
Releases a lock.
If the object has no lock, this function returns immediately.
This function returns \e true if, after it executes, the file is
not locked by this object, and \e false otherwise.
\sa lock(), isLocked(), lockMode()
*/
/*!
\fn QtLockedFile::~QtLockedFile()
Destroys the \e QtLockedFile object. If any locks were held, they
are released.
*/

View File

@@ -1,94 +0,0 @@
/****************************************************************************
**
** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
** All rights reserved.
**
** Contact: Nokia Corporation (qt-info@nokia.com)
**
** This file is part of a Qt Solutions component.
**
** You may use this file under the terms of the BSD license as follows:
**
** "Redistribution and use in source and binary forms, with or without
** modification, are permitted provided that the following conditions are
** met:
** * Redistributions of source code must retain the above copyright
** notice, this list of conditions and the following disclaimer.
** * Redistributions in binary form must reproduce the above copyright
** notice, this list of conditions and the following disclaimer in
** the documentation and/or other materials provided with the
** distribution.
** * Neither the name of Nokia Corporation and its Subsidiary(-ies) 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
** OWNER 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."
**
****************************************************************************/
#ifndef QTLOCKEDFILE_H
#define QTLOCKEDFILE_H
#include <QFile>
#ifdef Q_OS_WIN
#include <QVector>
#endif
#if defined(Q_WS_WIN) || defined(Q_OS_WIN)
# if !defined(QT_QTLOCKEDFILE_EXPORT) && !defined(QT_QTLOCKEDFILE_IMPORT)
# define QT_QTLOCKEDFILE_EXPORT
# elif defined(QT_QTLOCKEDFILE_IMPORT)
# if defined(QT_QTLOCKEDFILE_EXPORT)
# undef QT_QTLOCKEDFILE_EXPORT
# endif
# define QT_QTLOCKEDFILE_EXPORT __declspec(dllimport)
# elif defined(QT_QTLOCKEDFILE_EXPORT)
# undef QT_QTLOCKEDFILE_EXPORT
# define QT_QTLOCKEDFILE_EXPORT __declspec(dllexport)
# endif
#else
# define QT_QTLOCKEDFILE_EXPORT
#endif
class QT_QTLOCKEDFILE_EXPORT QtLockedFile : public QFile
{
public:
enum LockMode { NoLock = 0, ReadLock, WriteLock };
QtLockedFile();
QtLockedFile(const QString &name);
~QtLockedFile();
bool open(OpenMode mode);
bool lock(LockMode mode, bool block = true);
bool unlock();
bool isLocked() const;
LockMode lockMode() const;
private:
#ifdef Q_OS_WIN
Qt::HANDLE wmutex;
Qt::HANDLE rmutex;
QVector<Qt::HANDLE> rmutexes;
QString mutexname;
Qt::HANDLE getMutexHandle(int idx, bool doCreate);
bool waitMutex(Qt::HANDLE mutex, bool doBlock);
#endif
LockMode m_lock_mode;
};
#endif

View File

@@ -1,114 +0,0 @@
/****************************************************************************
**
** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
** All rights reserved.
**
** Contact: Nokia Corporation (qt-info@nokia.com)
**
** This file is part of a Qt Solutions component.
**
** You may use this file under the terms of the BSD license as follows:
**
** "Redistribution and use in source and binary forms, with or without
** modification, are permitted provided that the following conditions are
** met:
** * Redistributions of source code must retain the above copyright
** notice, this list of conditions and the following disclaimer.
** * Redistributions in binary form must reproduce the above copyright
** notice, this list of conditions and the following disclaimer in
** the documentation and/or other materials provided with the
** distribution.
** * Neither the name of Nokia Corporation and its Subsidiary(-ies) 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
** OWNER 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."
**
****************************************************************************/
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include <fcntl.h>
#include "qtlockedfile.h"
bool QtLockedFile::lock(LockMode mode, bool block)
{
if (!isOpen()) {
qWarning("QtLockedFile::lock(): file is not opened");
return false;
}
if (mode == NoLock)
return unlock();
if (mode == m_lock_mode)
return true;
if (m_lock_mode != NoLock)
unlock();
struct flock fl;
fl.l_whence = SEEK_SET;
fl.l_start = 0;
fl.l_len = 0;
fl.l_type = (mode == ReadLock) ? F_RDLCK : F_WRLCK;
int cmd = block ? F_SETLKW : F_SETLK;
int ret = fcntl(handle(), cmd, &fl);
if (ret == -1) {
if (errno != EINTR && errno != EAGAIN)
qWarning("QtLockedFile::lock(): fcntl: %s", strerror(errno));
return false;
}
m_lock_mode = mode;
return true;
}
bool QtLockedFile::unlock()
{
if (!isOpen()) {
qWarning("QtLockedFile::unlock(): file is not opened");
return false;
}
if (!isLocked())
return true;
struct flock fl;
fl.l_whence = SEEK_SET;
fl.l_start = 0;
fl.l_len = 0;
fl.l_type = F_UNLCK;
int ret = fcntl(handle(), F_SETLKW, &fl);
if (ret == -1) {
qWarning("QtLockedFile::lock(): fcntl: %s", strerror(errno));
return false;
}
m_lock_mode = NoLock;
return true;
}
QtLockedFile::~QtLockedFile()
{
if (isOpen())
unlock();
}

View File

@@ -1,203 +0,0 @@
/****************************************************************************
**
** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
** All rights reserved.
**
** Contact: Nokia Corporation (qt-info@nokia.com)
**
** This file is part of a Qt Solutions component.
**
** You may use this file under the terms of the BSD license as follows:
**
** "Redistribution and use in source and binary forms, with or without
** modification, are permitted provided that the following conditions are
** met:
** * Redistributions of source code must retain the above copyright
** notice, this list of conditions and the following disclaimer.
** * Redistributions in binary form must reproduce the above copyright
** notice, this list of conditions and the following disclaimer in
** the documentation and/or other materials provided with the
** distribution.
** * Neither the name of Nokia Corporation and its Subsidiary(-ies) 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
** OWNER 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."
**
****************************************************************************/
#include <QFileInfo>
#include "qtlockedfile.h"
#include <qt_windows.h>
#define MUTEX_PREFIX "QtLockedFile mutex "
// Maximum number of concurrent read locks. Must not be greater than MAXIMUM_WAIT_OBJECTS
#define MAX_READERS MAXIMUM_WAIT_OBJECTS
Qt::HANDLE QtLockedFile::getMutexHandle(int idx, bool doCreate)
{
if (mutexname.isEmpty()) {
QFileInfo fi(*this);
mutexname = QString::fromLatin1(MUTEX_PREFIX) + fi.absoluteFilePath().toLower();
}
QString mname(mutexname);
if (idx >= 0)
mname += QString::number(idx);
Qt::HANDLE mutex;
if (doCreate) {
mutex = CreateMutexW(NULL, FALSE, (WCHAR*)mname.utf16());
if (!mutex) {
qErrnoWarning("QtLockedFile::lock(): CreateMutex failed");
return 0;
}
}
else {
mutex = OpenMutexW(SYNCHRONIZE | MUTEX_MODIFY_STATE, FALSE, (WCHAR*)mname.utf16());
if (!mutex) {
if (GetLastError() != ERROR_FILE_NOT_FOUND)
qErrnoWarning("QtLockedFile::lock(): OpenMutex failed");
return 0;
}
}
return mutex;
}
bool QtLockedFile::waitMutex(Qt::HANDLE mutex, bool doBlock)
{
Q_ASSERT(mutex);
DWORD res = WaitForSingleObject(mutex, doBlock ? INFINITE : 0);
switch (res) {
case WAIT_OBJECT_0:
case WAIT_ABANDONED:
return true;
break;
case WAIT_TIMEOUT:
break;
default:
qErrnoWarning("QtLockedFile::lock(): WaitForSingleObject failed");
}
return false;
}
bool QtLockedFile::lock(LockMode mode, bool block)
{
if (!isOpen()) {
qWarning("QtLockedFile::lock(): file is not opened");
return false;
}
if (mode == NoLock)
return unlock();
if (mode == m_lock_mode)
return true;
if (m_lock_mode != NoLock)
unlock();
if (!wmutex && !(wmutex = getMutexHandle(-1, true)))
return false;
if (!waitMutex(wmutex, block))
return false;
if (mode == ReadLock) {
int idx = 0;
for (; idx < MAX_READERS; idx++) {
rmutex = getMutexHandle(idx, false);
if (!rmutex || waitMutex(rmutex, false))
break;
CloseHandle(rmutex);
}
bool ok = true;
if (idx >= MAX_READERS) {
qWarning("QtLockedFile::lock(): too many readers");
rmutex = 0;
ok = false;
}
else if (!rmutex) {
rmutex = getMutexHandle(idx, true);
if (!rmutex || !waitMutex(rmutex, false))
ok = false;
}
if (!ok && rmutex) {
CloseHandle(rmutex);
rmutex = 0;
}
ReleaseMutex(wmutex);
if (!ok)
return false;
}
else {
Q_ASSERT(rmutexes.isEmpty());
for (int i = 0; i < MAX_READERS; i++) {
Qt::HANDLE mutex = getMutexHandle(i, false);
if (mutex)
rmutexes.append(mutex);
}
if (rmutexes.size()) {
DWORD res = WaitForMultipleObjects(rmutexes.size(), rmutexes.constData(), TRUE, block ? INFINITE : 0);
if (res != WAIT_OBJECT_0 && res != WAIT_ABANDONED) {
if (res != WAIT_TIMEOUT)
qErrnoWarning("QtLockedFile::lock(): WaitForMultipleObjects failed");
m_lock_mode = WriteLock; // trick unlock() to clean up - semiyucky
unlock();
return false;
}
}
}
m_lock_mode = mode;
return true;
}
bool QtLockedFile::unlock()
{
if (!isOpen()) {
qWarning("QtLockedFile::unlock(): file is not opened");
return false;
}
if (!isLocked())
return true;
if (m_lock_mode == ReadLock) {
ReleaseMutex(rmutex);
CloseHandle(rmutex);
rmutex = 0;
}
else {
foreach(Qt::HANDLE mutex, rmutexes) {
ReleaseMutex(mutex);
CloseHandle(mutex);
}
rmutexes.clear();
ReleaseMutex(wmutex);
}
m_lock_mode = QtLockedFile::NoLock;
return true;
}
QtLockedFile::~QtLockedFile()
{
if (isOpen())
unlock();
if (wmutex)
CloseHandle(wmutex);
}

View File

@@ -1,335 +0,0 @@
/****************************************************************************
**
** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
** All rights reserved.
**
** Contact: Nokia Corporation (qt-info@nokia.com)
**
** This file is part of a Qt Solutions component.
**
** You may use this file under the terms of the BSD license as follows:
**
** "Redistribution and use in source and binary forms, with or without
** modification, are permitted provided that the following conditions are
** met:
** * Redistributions of source code must retain the above copyright
** notice, this list of conditions and the following disclaimer.
** * Redistributions in binary form must reproduce the above copyright
** notice, this list of conditions and the following disclaimer in
** the documentation and/or other materials provided with the
** distribution.
** * Neither the name of Nokia Corporation and its Subsidiary(-ies) 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
** OWNER 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."
**
****************************************************************************/
#include "qtsingleapplication.h"
#include <QApplication>
#include <QWidget>
#include <QString>
#include "qtlocalpeer.h"
/*!
\class QtSingleApplication qtsingleapplication.h
\brief The QtSingleApplication class provides an API to detect and
communicate with running instances of an application.
This class allows you to create applications where only one
instance should be running at a time. I.e., if the user tries to
launch another instance, the already running instance will be
activated instead. Another usecase is a client-server system,
where the first started instance will assume the role of server,
and the later instances will act as clients of that server.
By default, the full path of the executable file is used to
determine whether two processes are instances of the same
application. You can also provide an explicit identifier string
that will be compared instead.
The application should create the QtSingleApplication object early
in the startup phase, and call isRunning() to find out if another
instance of this application is already running. If isRunning()
returns false, it means that no other instance is running, and
this instance has assumed the role as the running instance. In
this case, the application should continue with the initialization
of the application user interface before entering the event loop
with exec(), as normal.
The messageReceived() signal will be emitted when the running
application receives messages from another instance of the same
application. When a message is received it might be helpful to the
user to raise the application so that it becomes visible. To
facilitate this, QtSingleApplication provides the
setActivationWindow() function and the activateWindow() slot.
If isRunning() returns true, another instance is already
running. It may be alerted to the fact that another instance has
started by using the sendMessage() function. Also data such as
startup parameters (e.g. the name of the file the user wanted this
new instance to open) can be passed to the running instance with
this function. Then, the application should terminate (or enter
client mode).
If isRunning() returns true, but sendMessage() fails, that is an
indication that the running instance is frozen.
Here's an example that shows how to convert an existing
application to use QtSingleApplication. It is very simple and does
not make use of all QtSingleApplication's functionality (see the
examples for that).
\code
// Original
int main(int argc, char **argv)
{
QApplication app(argc, argv);
MyMainWidget mmw;
mmw.show();
return app.exec();
}
// Single instance
int main(int argc, char **argv)
{
QtSingleApplication app(argc, argv);
if (app.isRunning())
return !app.sendMessage(someDataString);
MyMainWidget mmw;
app.setActivationWindow(&mmw);
mmw.show();
return app.exec();
}
\endcode
Once this QtSingleApplication instance is destroyed (normally when
the process exits or crashes), when the user next attempts to run the
application this instance will not, of course, be encountered. The
next instance to call isRunning() or sendMessage() will assume the
role as the new running instance.
For console (non-GUI) applications, QtSingleCoreApplication may be
used instead of this class, to avoid the dependency on the QtGui
library.
\sa QtSingleCoreApplication
*/
void QtSingleApplication::sysInit(const QString &appId)
{
actWin = 0;
peer = new QtLocalPeer(this, appId);
connect(peer, SIGNAL(messageReceived(const QString&)), SIGNAL(messageReceived(const QString&)));
}
/*!
Creates a QtSingleApplication object. The application identifier
will be QCoreApplication::applicationFilePath(). \a argc, \a
argv, and \a GUIenabled are passed on to the QAppliation constructor.
If you are creating a console application (i.e. setting \a
GUIenabled to false), you may consider using
QtSingleCoreApplication instead.
*/
QtSingleApplication::QtSingleApplication(int &argc, char **argv, bool GUIenabled)
: QApplication(argc, argv, GUIenabled)
{
sysInit();
}
/*!
Creates a QtSingleApplication object with the application
identifier \a appId. \a argc and \a argv are passed on to the
QAppliation constructor.
*/
QtSingleApplication::QtSingleApplication(const QString &appId, int &argc, char **argv)
: QApplication(argc, argv)
{
sysInit(appId);
}
#if defined(Q_WS_X11)
/*!
Special constructor for X11, ref. the documentation of
QApplication's corresponding constructor. The application identifier
will be QCoreApplication::applicationFilePath(). \a dpy, \a visual,
and \a cmap are passed on to the QApplication constructor.
*/
QtSingleApplication::QtSingleApplication(Display* dpy, Qt::HANDLE visual, Qt::HANDLE cmap)
: QApplication(dpy, visual, cmap)
{
sysInit();
}
/*!
Special constructor for X11, ref. the documentation of
QApplication's corresponding constructor. The application identifier
will be QCoreApplication::applicationFilePath(). \a dpy, \a argc, \a
argv, \a visual, and \a cmap are passed on to the QApplication
constructor.
*/
QtSingleApplication::QtSingleApplication(Display *dpy, int &argc, char **argv, Qt::HANDLE visual, Qt::HANDLE cmap)
: QApplication(dpy, argc, argv, visual, cmap)
{
sysInit();
}
/*!
Special constructor for X11, ref. the documentation of
QApplication's corresponding constructor. The application identifier
will be \a appId. \a dpy, \a argc, \a
argv, \a visual, and \a cmap are passed on to the QApplication
constructor.
*/
QtSingleApplication::QtSingleApplication(Display* dpy, const QString &appId, int argc, char **argv, Qt::HANDLE visual, Qt::HANDLE cmap)
: QApplication(dpy, argc, argv, visual, cmap)
{
sysInit(appId);
}
#endif
/*!
Returns true if another instance of this application is running;
otherwise false.
This function does not find instances of this application that are
being run by a different user (on Windows: that are running in
another session).
\sa sendMessage()
*/
bool QtSingleApplication::isRunning()
{
return peer->isClient();
}
/*!
Tries to send the text \a message to the currently running
instance. The QtSingleApplication object in the running instance
will emit the messageReceived() signal when it receives the
message.
This function returns true if the message has been sent to, and
processed by, the current instance. If there is no instance
currently running, or if the running instance fails to process the
message within \a timeout milliseconds, this function return false.
\sa isRunning(), messageReceived()
*/
bool QtSingleApplication::sendMessage(const QString &message, int timeout)
{
return peer->sendMessage(message, timeout);
}
/*!
Returns the application identifier. Two processes with the same
identifier will be regarded as instances of the same application.
*/
QString QtSingleApplication::id() const
{
return peer->applicationId();
}
/*!
Sets the activation window of this application to \a aw. The
activation window is the widget that will be activated by
activateWindow(). This is typically the application's main window.
If \a activateOnMessage is true (the default), the window will be
activated automatically every time a message is received, just prior
to the messageReceived() signal being emitted.
\sa activateWindow(), messageReceived()
*/
void QtSingleApplication::setActivationWindow(QWidget* aw, bool activateOnMessage)
{
actWin = aw;
if (activateOnMessage)
connect(peer, SIGNAL(messageReceived(const QString&)), this, SLOT(activateWindow()));
else
disconnect(peer, SIGNAL(messageReceived(const QString&)), this, SLOT(activateWindow()));
}
/*!
Returns the applications activation window if one has been set by
calling setActivationWindow(), otherwise returns 0.
\sa setActivationWindow()
*/
QWidget* QtSingleApplication::activationWindow() const
{
return actWin;
}
/*!
De-minimizes, raises, and activates this application's activation window.
This function does nothing if no activation window has been set.
This is a convenience function to show the user that this
application instance has been activated when he has tried to start
another instance.
This function should typically be called in response to the
messageReceived() signal. By default, that will happen
automatically, if an activation window has been set.
\sa setActivationWindow(), messageReceived(), initialize()
*/
void QtSingleApplication::activateWindow()
{
if (actWin) {
actWin->setWindowState(actWin->windowState() & ~Qt::WindowMinimized);
actWin->raise();
actWin->activateWindow();
}
}
/*!
\fn void QtSingleApplication::messageReceived(const QString& message)
This signal is emitted when the current instance receives a \a
message from another instance of this application.
\sa sendMessage(), setActivationWindow(), activateWindow()
*/
/*!
\fn void QtSingleApplication::initialize(bool dummy = true)
\obsolete
*/

View File

@@ -1,104 +0,0 @@
/****************************************************************************
**
** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
** All rights reserved.
**
** Contact: Nokia Corporation (qt-info@nokia.com)
**
** This file is part of a Qt Solutions component.
**
** You may use this file under the terms of the BSD license as follows:
**
** "Redistribution and use in source and binary forms, with or without
** modification, are permitted provided that the following conditions are
** met:
** * Redistributions of source code must retain the above copyright
** notice, this list of conditions and the following disclaimer.
** * Redistributions in binary form must reproduce the above copyright
** notice, this list of conditions and the following disclaimer in
** the documentation and/or other materials provided with the
** distribution.
** * Neither the name of Nokia Corporation and its Subsidiary(-ies) 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
** OWNER 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."
**
****************************************************************************/
#ifndef QTSINGLEAPPLICATION_H
#define QTSINGLEAPPLICATION_H
#include <QtGlobal>
#include <QObject>
#include <QWidget>
#include <QApplication>
#include <QString>
class QtLocalPeer;
#if defined(Q_WS_WIN) || defined(Q_OS_WIN32)
# if !defined(QT_QTSINGLEAPPLICATION_EXPORT) && !defined(QT_QTSINGLEAPPLICATION_IMPORT)
# define QT_QTSINGLEAPPLICATION_EXPORT
# elif defined(QT_QTSINGLEAPPLICATION_IMPORT)
# if defined(QT_QTSINGLEAPPLICATION_EXPORT)
# undef QT_QTSINGLEAPPLICATION_EXPORT
# endif
# define QT_QTSINGLEAPPLICATION_EXPORT __declspec(dllimport)
# elif defined(QT_QTSINGLEAPPLICATION_EXPORT)
# undef QT_QTSINGLEAPPLICATION_EXPORT
# define QT_QTSINGLEAPPLICATION_EXPORT __declspec(dllexport)
# endif
#else
# define QT_QTSINGLEAPPLICATION_EXPORT
#endif
class QT_QTSINGLEAPPLICATION_EXPORT QtSingleApplication : public QApplication
{
Q_OBJECT
public:
QtSingleApplication(int &argc, char **argv, bool GUIenabled = true);
QtSingleApplication(const QString &id, int &argc, char **argv);
#if defined(Q_WS_X11)
QtSingleApplication(Display* dpy, Qt::HANDLE visual = 0, Qt::HANDLE colormap = 0);
QtSingleApplication(Display *dpy, int &argc, char **argv, Qt::HANDLE visual = 0, Qt::HANDLE cmap= 0);
QtSingleApplication(Display* dpy, const QString &appId, int argc, char **argv, Qt::HANDLE visual = 0, Qt::HANDLE colormap = 0);
#endif
bool isRunning();
QString id() const;
void setActivationWindow(QWidget* aw, bool activateOnMessage = true);
QWidget* activationWindow() const;
// Obsolete:
void initialize(bool dummy = true)
{ isRunning(); Q_UNUSED(dummy) }
public Q_SLOTS:
bool sendMessage(const QString &message, int timeout = 5000);
void activateWindow();
Q_SIGNALS:
void messageReceived(const QString &message);
private:
void sysInit(const QString &appId = QString());
QtLocalPeer *peer;
QWidget *actWin;
};
#endif // QTSINGLEAPPLICATION_H

View File

@@ -1,146 +0,0 @@
/****************************************************************************
**
** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
** All rights reserved.
**
** Contact: Nokia Corporation (qt-info@nokia.com)
**
** This file is part of a Qt Solutions component.
**
** You may use this file under the terms of the BSD license as follows:
**
** "Redistribution and use in source and binary forms, with or without
** modification, are permitted provided that the following conditions are
** met:
** * Redistributions of source code must retain the above copyright
** notice, this list of conditions and the following disclaimer.
** * Redistributions in binary form must reproduce the above copyright
** notice, this list of conditions and the following disclaimer in
** the documentation and/or other materials provided with the
** distribution.
** * Neither the name of Nokia Corporation and its Subsidiary(-ies) 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
** OWNER 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."
**
****************************************************************************/
#include <QCoreApplication>
#include <QString>
#include <QFlags>
#include "qtsinglecoreapplication.h"
#include "qtlocalpeer.h"
/*!
\class QtSingleCoreApplication qtsinglecoreapplication.h
\brief A variant of the QtSingleApplication class for non-GUI applications.
This class is a variant of QtSingleApplication suited for use in
console (non-GUI) applications. It is an extension of
QCoreApplication (instead of QApplication). It does not require
the QtGui library.
The API and usage is identical to QtSingleApplication, except that
functions relating to the "activation window" are not present, for
obvious reasons. Please refer to the QtSingleApplication
documentation for explanation of the usage.
A QtSingleCoreApplication instance can communicate to a
QtSingleApplication instance if they share the same application
id. Hence, this class can be used to create a light-weight
command-line tool that sends commands to a GUI application.
\sa QtSingleApplication
*/
/*!
Creates a QtSingleCoreApplication object. The application identifier
will be QCoreApplication::applicationFilePath(). \a argc and \a
argv are passed on to the QCoreAppliation constructor.
*/
QtSingleCoreApplication::QtSingleCoreApplication(int &argc, char **argv)
: QCoreApplication(argc, argv) {
peer = new QtLocalPeer(this);
connect(peer, SIGNAL(messageReceived(const QString&)), SIGNAL(messageReceived(const QString&)));
}
/*!
Creates a QtSingleCoreApplication object with the application
identifier \a appId. \a argc and \a argv are passed on to the
QCoreAppliation constructor.
*/
QtSingleCoreApplication::QtSingleCoreApplication(const QString &appId, int &argc, char **argv)
: QCoreApplication(argc, argv) {
peer = new QtLocalPeer(this, appId);
connect(peer, SIGNAL(messageReceived(const QString&)), SIGNAL(messageReceived(const QString&)));
}
/*!
Returns true if another instance of this application is running;
otherwise false.
This function does not find instances of this application that are
being run by a different user (on Windows: that are running in
another session).
\sa sendMessage()
*/
bool QtSingleCoreApplication::isRunning() {
return peer->isClient();
}
/*!
Tries to send the text \a message to the currently running
instance. The QtSingleCoreApplication object in the running instance
will emit the messageReceived() signal when it receives the
message.
This function returns true if the message has been sent to, and
processed by, the current instance. If there is no instance
currently running, or if the running instance fails to process the
message within \a timeout milliseconds, this function return false.
\sa isRunning(), messageReceived()
*/
bool QtSingleCoreApplication::sendMessage(const QString &message, int timeout) {
return peer->sendMessage(message, timeout);
}
/*!
Returns the application identifier. Two processes with the same
identifier will be regarded as instances of the same application.
*/
QString QtSingleCoreApplication::id() const {
return peer->applicationId();
}
/*!
\fn void QtSingleCoreApplication::messageReceived(const QString& message)
This signal is emitted when the current instance receives a \a
message from another instance of this application.
\sa sendMessage()
*/

View File

@@ -1,72 +0,0 @@
/****************************************************************************
**
** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
** All rights reserved.
**
** Contact: Nokia Corporation (qt-info@nokia.com)
**
** This file is part of a Qt Solutions component.
**
** You may use this file under the terms of the BSD license as follows:
**
** "Redistribution and use in source and binary forms, with or without
** modification, are permitted provided that the following conditions are
** met:
** * Redistributions of source code must retain the above copyright
** notice, this list of conditions and the following disclaimer.
** * Redistributions in binary form must reproduce the above copyright
** notice, this list of conditions and the following disclaimer in
** the documentation and/or other materials provided with the
** distribution.
** * Neither the name of Nokia Corporation and its Subsidiary(-ies) 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
** OWNER 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."
**
****************************************************************************/
#ifndef QTSINGLECOREAPPLICATION_H
#define QTSINGLECOREAPPLICATION_H
#include <QCoreApplication>
#include <QObject>
#include <QString>
class QtLocalPeer;
class QtSingleCoreApplication : public QCoreApplication
{
Q_OBJECT
public:
QtSingleCoreApplication(int &argc, char **argv);
QtSingleCoreApplication(const QString &id, int &argc, char **argv);
bool isRunning();
QString id() const;
public Q_SLOTS:
bool sendMessage(const QString &message, int timeout = 5000);
Q_SIGNALS:
void messageReceived(const QString &message);
private:
QtLocalPeer* peer;
};
#endif // QTSINGLECOREAPPLICATION_H

View File

@@ -1,14 +0,0 @@
cmake_minimum_required(VERSION 2.8.11)
set(CMAKE_CXX_STANDARD 11)
set(QTWIN-SOURCES
qtwin.cpp
)
ADD_LIBRARY(qtwin STATIC
${QTWIN-SOURCES}
)
target_link_libraries(qtwin
Qt5::Widgets
)

View File

@@ -1,229 +0,0 @@
/****************************************************************************
**
** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
**
** Use, modification and distribution is allowed without limitation,
** warranty, liability or support of any kind.
**
****************************************************************************/
#include "qtwin.h"
#include <QLibrary>
#include <QApplication>
#include <QWidget>
#include <QList>
#include <QPointer>
#ifdef Q_WS_WIN
#include <qt_windows.h>
// Blur behind data structures
#define DWM_BB_ENABLE 0x00000001 // fEnable has been specified
#define DWM_BB_BLURREGION 0x00000002 // hRgnBlur has been specified
#define DWM_BB_TRANSITIONONMAXIMIZED 0x00000004 // fTransitionOnMaximized has been specified
#define WM_DWMCOMPOSITIONCHANGED 0x031E // Composition changed window message
typedef struct _DWM_BLURBEHIND
{
DWORD dwFlags;
BOOL fEnable;
HRGN hRgnBlur;
BOOL fTransitionOnMaximized;
} DWM_BLURBEHIND, *PDWM_BLURBEHIND;
typedef struct _MARGINS
{
int cxLeftWidth;
int cxRightWidth;
int cyTopHeight;
int cyBottomHeight;
} MARGINS, *PMARGINS;
typedef HRESULT (WINAPI *PtrDwmIsCompositionEnabled)(BOOL* pfEnabled);
typedef HRESULT (WINAPI *PtrDwmExtendFrameIntoClientArea)(HWND hWnd, const MARGINS* pMarInset);
typedef HRESULT (WINAPI *PtrDwmEnableBlurBehindWindow)(HWND hWnd, const DWM_BLURBEHIND* pBlurBehind);
typedef HRESULT (WINAPI *PtrDwmGetColorizationColor)(DWORD *pcrColorization, BOOL *pfOpaqueBlend);
static PtrDwmIsCompositionEnabled pDwmIsCompositionEnabled= 0;
static PtrDwmEnableBlurBehindWindow pDwmEnableBlurBehindWindow = 0;
static PtrDwmExtendFrameIntoClientArea pDwmExtendFrameIntoClientArea = 0;
static PtrDwmGetColorizationColor pDwmGetColorizationColor = 0;
/*
* Internal helper class that notifies windows if the
* DWM compositing state changes and updates the widget
* flags correspondingly.
*/
class WindowNotifier : public QWidget
{
public:
WindowNotifier() { winId(); }
void addWidget(QWidget *widget) { widgets.append(widget); }
void removeWidget(QWidget *widget) { widgets.removeAll(widget); }
bool winEvent(MSG *message, long *result);
private:
QWidgetList widgets;
};
static bool resolveLibs()
{
if (!pDwmIsCompositionEnabled) {
QLibrary dwmLib(QString::fromAscii("dwmapi"));
pDwmIsCompositionEnabled =(PtrDwmIsCompositionEnabled)dwmLib.resolve("DwmIsCompositionEnabled");
pDwmExtendFrameIntoClientArea = (PtrDwmExtendFrameIntoClientArea)dwmLib.resolve("DwmExtendFrameIntoClientArea");
pDwmEnableBlurBehindWindow = (PtrDwmEnableBlurBehindWindow)dwmLib.resolve("DwmEnableBlurBehindWindow");
pDwmGetColorizationColor = (PtrDwmGetColorizationColor)dwmLib.resolve("DwmGetColorizationColor");
}
return pDwmIsCompositionEnabled != 0;
}
#endif
/*!
* Chekcs and returns true if Windows DWM composition
* is currently enabled on the system.
*
* To get live notification on the availability of
* this feature, you will currently have to
* reimplement winEvent() on your widget and listen
* for the WM_DWMCOMPOSITIONCHANGED event to occur.
*
*/
bool QtWin::isCompositionEnabled()
{
#ifdef Q_WS_WIN
if (resolveLibs()) {
HRESULT hr = S_OK;
BOOL isEnabled = false;
hr = pDwmIsCompositionEnabled(&isEnabled);
if (SUCCEEDED(hr))
return isEnabled;
}
#endif
return false;
}
/*!
* Enables Blur behind on a Widget.
*
* \a enable tells if the blur should be enabled or not
*/
bool QtWin::enableBlurBehindWindow(QWidget *widget, bool enable,
const QRegion &region)
{
Q_ASSERT(widget);
bool result = false;
#ifdef Q_WS_WIN
if (resolveLibs()) {
DWM_BLURBEHIND bb = {0};
HRESULT hr = S_OK;
bb.fEnable = enable;
bb.dwFlags = DWM_BB_ENABLE;
bb.hRgnBlur = NULL;
if (!region.isEmpty()) {
bb.dwFlags |= DWM_BB_BLURREGION;
bb.hRgnBlur = region.handle();
}
widget->setAttribute(Qt::WA_TranslucentBackground, enable);
widget->setAttribute(Qt::WA_NoSystemBackground, enable);
hr = pDwmEnableBlurBehindWindow(widget->winId(), &bb);
if (SUCCEEDED(hr)) {
result = true;
windowNotifier()->addWidget(widget);
}
}
#endif
return result;
}
/*!
* ExtendFrameIntoClientArea.
*
* This controls the rendering of the frame inside the window.
* Note that passing margins of -1 (the default value) will completely
* remove the frame from the window.
*
* \note you should not call enableBlurBehindWindow before calling
* this functions
*
* \a enable tells if the blur should be enabled or not
*/
bool QtWin::extendFrameIntoClientArea(QWidget *widget, int left, int top, int right, int bottom)
{
Q_ASSERT(widget);
Q_UNUSED(left);
Q_UNUSED(top);
Q_UNUSED(right);
Q_UNUSED(bottom);
bool result = false;
#ifdef Q_WS_WIN
if (resolveLibs()) {
QLibrary dwmLib(QString::fromAscii("dwmapi"));
HRESULT hr = S_OK;
MARGINS m = {left, top, right, bottom};
hr = pDwmExtendFrameIntoClientArea(widget->winId(), &m);
if (SUCCEEDED(hr)) {
result = true;
windowNotifier()->addWidget(widget);
}
widget->setAttribute(Qt::WA_TranslucentBackground, result);
}
#endif
return result;
}
/*!
* Returns the current colorizationColor for the window.
*
* \a enable tells if the blur should be enabled or not
*/
QColor QtWin::colorizatinColor()
{
QColor resultColor = QApplication::palette().window().color();
#ifdef Q_WS_WIN
if (resolveLibs()) {
DWORD color = 0;
BOOL opaque = FALSE;
QLibrary dwmLib(QString::fromAscii("dwmapi"));
HRESULT hr = S_OK;
hr = pDwmGetColorizationColor(&color, &opaque);
if (SUCCEEDED(hr))
resultColor = QColor(color);
}
#endif
return resultColor;
}
#ifdef Q_WS_WIN
WindowNotifier *QtWin::windowNotifier()
{
static WindowNotifier *windowNotifierInstance = 0;
if (!windowNotifierInstance)
windowNotifierInstance = new WindowNotifier;
return windowNotifierInstance;
}
/* Notify all enabled windows that the DWM state changed */
bool WindowNotifier::winEvent(MSG *message, long *result)
{
if (message && message->message == WM_DWMCOMPOSITIONCHANGED) {
bool compositionEnabled = QtWin::isCompositionEnabled();
foreach(QWidget * widget, widgets) {
if (widget) {
widget->setAttribute(Qt::WA_NoSystemBackground, compositionEnabled);
}
widget->update();
}
}
return QWidget::winEvent(message, result);
}
#endif

View File

@@ -1,38 +0,0 @@
/****************************************************************************
**
** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
**
** Use, modification and distribution is allowed without limitation,
** warranty, liability or support of any kind.
**
****************************************************************************/
#ifndef QTWIN_H
#define QTWIN_H
#include <QColor>
#include <QWidget>
/**
* This is a helper class for using the Desktop Window Manager
* functionality on Windows 7 and Windows Vista. On other platforms
* these functions will simply not do anything.
*/
class WindowNotifier;
class QtWin
{
public:
static bool enableBlurBehindWindow(QWidget *widget, bool enable = true,
const QRegion& region = QRegion());
static bool extendFrameIntoClientArea(QWidget *widget,
int left = -1, int top = -1,
int right = -1, int bottom = -1);
static bool isCompositionEnabled();
static QColor colorizatinColor();
private:
static WindowNotifier *windowNotifier();
};
#endif // QTWIN_H

View File

@@ -1,36 +0,0 @@
cmake_minimum_required(VERSION 2.8.11)
set(CMAKE_CXX_STANDARD 11)
set(QXT-SOURCES
qxtglobal.cpp
qxtglobalshortcut.cpp
)
set(QXT-MOC-HEADERS
qxtglobalshortcut.h
)
find_package(X11)
include_directories(${X11_INCLUDE_DIR})
include_directories(${Qt5Gui_PRIVATE_INCLUDE_DIRS})
if(WIN32)
set(QXT-SOURCES ${QXT-SOURCES} qxtglobalshortcut_win.cpp)
elseif(APPLE)
set(QXT-SOURCES ${QXT-SOURCES} qxtglobalshortcut_mac.cpp)
else(WIN32)
set(QXT-SOURCES ${QXT-SOURCES} qxtglobalshortcut_x11.cpp)
endif(WIN32)
QT5_WRAP_CPP(QXT-SOURCES-MOC ${QXT-MOC-HEADERS})
ADD_LIBRARY(qxt STATIC
${QXT-SOURCES}
${QXT-SOURCES-MOC}
)
if(WIN32)
target_link_libraries(qxt Qt5::Core Qt5::Widgets)
else(WIN32)
target_link_libraries(qxt Qt5::Core Qt5::Widgets Qt5::X11Extras)
endif(WIN32)

89
3rdparty/qxt/LICENSE vendored
View File

@@ -1,89 +0,0 @@
Qt Extension Library
Copyright (C) 2007 Qxt Foundation
------------------- Disclaimer ------------------------------------------------
Until the Qxt Foundation is legally established, copyright for the
source code falls back to the original contributor. For information about the
status of the Qxt Foundation, or about the copyright status of any part of Qxt,
contact the Qxt project maintainers at <foundation@libqxt.org>
Once the Qxt Foundation has been legally established, all contributors must
transfer all copyright interest to the Qxt Foundation before their submissions
will be added to the project.
------------------- License ---------------------------------------------------
This library is free software; you can redistribute it and/or modify it
under the terms of the Common Public License, version 1.0, as published by IBM
or under the terms of the GNU Lesser General Public License, version 2.1,
as published by the Free Software Foundation
This file is provided "AS IS", without WARRANTIES OR CONDITIONS OF ANY
KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT LIMITATION, ANY
WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT, MERCHANTABILITY OR
FITNESS FOR A PARTICULAR PURPOSE.
You should have received a copy of the CPL along with this file.
See the LICENSE file and the cpl1.0.txt file included with the source
distribution for more information. If you did not receive a copy of the
license, contact the Qxt Foundation.
You should have received a copy of the LGPL along with this file.
See the LICENSE file and the lgpl-2.1.txt file included with the source
distribution for more information. If you did not receive a copy of the
license, contact the Qxt Foundation.
Parts of Qxt depend on Qt 4 and/or other libraries that have their own
licenses. Qxt is independent of these licenses; however, use of these other
libraries is subject to their respective license agreements.
------------------- Intent ----------------------------------------------------
The following section describes the opinions and intent of the Qxt Foundation
with regards to the licensing and use of the Qxt source code and library. In
the event that the CPL is found to be illegal or invalid, or if any application
or clause of the license is subjected to question or abuse, this section is a
general statement of the desired interpretation.
This section has no legal standing and the statements made here are strictly
subject to the text of the CPL; that is, if this section and the CPL are in
disagreement, the text of the CPL takes precedence. In no way does this
intent grant you any additional rights or impose any additional restrictions.
If you have questions about licensing, contact the maintainers.
Qxt is built and supported by open-source enthusiasts.
- Please respect the open-source background of the contributors. The code is
provided for everyone's use; you may not restrict the rights of anyone to
use it.
- No individual may claim ownership of any part of the code. It belongs
to the community.
- You may modify the source code to suit your needs, but these changes
must be made free. If you distribute a modified form of Qxt, you must
also distribute the entire source code of the modified form.
- Digital Rights Management (DRM) puts unfair, unfree restrictions on
users and developers. It is the opposite of Free Software. We can't
stop you from using it, but please don't use the Qxt name for software
restricted by DRM.
- Please respect the time and effort put into the project by the developers.
- If you find Qxt useful, it would be appreciated if you would include
text in your application (for instance, in the About dialog) giving
acknowledgement to Qxt.
- If you make modifications to the source code, you must not call the
modified version "Qxt." It's okay to include "Qxt" in the name, but
anyone who receives the modified version needs to know that it's not
the same as the version distributed by the Qxt Foundation.
- We want everyone to be able to use Qxt without restrictions.
- If you distribute Qxt in compiled binary form, please ensure that
everyone who receives it can get the source code used to create it.
- You are free to use Qxt in closed-source applications as long as you
distribute Qxt in an open-source fashion. This does not require you
to make your entire application open-source.
- The Qxt Foundation is a non-profit, non-political organization.
- Please don't use the Qxt name in any political or semi-political
propaganda or publication. We don't like it.
- Qxt is distributed "as-is," with no warranty.
- If it makes your program crash, your computer blow up, or tiny demons
fly out of your nose, please don't sue us.

View File

@@ -1,372 +0,0 @@
#ifndef KEYMAPPER_X11_H
#define KEYMAPPER_X11_H
// (davidsansome) Nicked from qkeymapper_x11.cpp
#include <Qt>
#define XK_MISCELLANY
#define XK_LATIN1
#define XK_KOREAN
#define XK_XKB_KEYS
#include <X11/keysymdef.h>
//
// Keyboard event translation
//
#ifndef XK_ISO_Left_Tab
#define XK_ISO_Left_Tab 0xFE20
#endif
#ifndef XK_dead_hook
#define XK_dead_hook 0xFE61
#endif
#ifndef XK_dead_horn
#define XK_dead_horn 0xFE62
#endif
#ifndef XK_Codeinput
#define XK_Codeinput 0xFF37
#endif
#ifndef XK_Kanji_Bangou
#define XK_Kanji_Bangou 0xFF37 /* same as codeinput */
#endif
// Fix old X libraries
#ifndef XK_KP_Home
#define XK_KP_Home 0xFF95
#endif
#ifndef XK_KP_Left
#define XK_KP_Left 0xFF96
#endif
#ifndef XK_KP_Up
#define XK_KP_Up 0xFF97
#endif
#ifndef XK_KP_Right
#define XK_KP_Right 0xFF98
#endif
#ifndef XK_KP_Down
#define XK_KP_Down 0xFF99
#endif
#ifndef XK_KP_Prior
#define XK_KP_Prior 0xFF9A
#endif
#ifndef XK_KP_Next
#define XK_KP_Next 0xFF9B
#endif
#ifndef XK_KP_End
#define XK_KP_End 0xFF9C
#endif
#ifndef XK_KP_Insert
#define XK_KP_Insert 0xFF9E
#endif
#ifndef XK_KP_Delete
#define XK_KP_Delete 0xFF9F
#endif
// the next lines are taken from XFree > 4.0 (X11/XF86keysyms.h), defining some special
// multimedia keys. They are included here as not every system has them.
#define XF86XK_Standby 0x1008FF10
#define XF86XK_AudioLowerVolume 0x1008FF11
#define XF86XK_AudioMute 0x1008FF12
#define XF86XK_AudioRaiseVolume 0x1008FF13
#define XF86XK_AudioPlay 0x1008FF14
#define XF86XK_AudioStop 0x1008FF15
#define XF86XK_AudioPrev 0x1008FF16
#define XF86XK_AudioNext 0x1008FF17
#define XF86XK_HomePage 0x1008FF18
#define XF86XK_Calculator 0x1008FF1D
#define XF86XK_Mail 0x1008FF19
#define XF86XK_Start 0x1008FF1A
#define XF86XK_Search 0x1008FF1B
#define XF86XK_AudioRecord 0x1008FF1C
#define XF86XK_Back 0x1008FF26
#define XF86XK_Forward 0x1008FF27
#define XF86XK_Stop 0x1008FF28
#define XF86XK_Refresh 0x1008FF29
#define XF86XK_Favorites 0x1008FF30
#define XF86XK_AudioPause 0x1008FF31
#define XF86XK_AudioMedia 0x1008FF32
#define XF86XK_MyComputer 0x1008FF33
#define XF86XK_OpenURL 0x1008FF38
#define XF86XK_Launch0 0x1008FF40
#define XF86XK_Launch1 0x1008FF41
#define XF86XK_Launch2 0x1008FF42
#define XF86XK_Launch3 0x1008FF43
#define XF86XK_Launch4 0x1008FF44
#define XF86XK_Launch5 0x1008FF45
#define XF86XK_Launch6 0x1008FF46
#define XF86XK_Launch7 0x1008FF47
#define XF86XK_Launch8 0x1008FF48
#define XF86XK_Launch9 0x1008FF49
#define XF86XK_LaunchA 0x1008FF4A
#define XF86XK_LaunchB 0x1008FF4B
#define XF86XK_LaunchC 0x1008FF4C
#define XF86XK_LaunchD 0x1008FF4D
#define XF86XK_LaunchE 0x1008FF4E
#define XF86XK_LaunchF 0x1008FF4F
// end of XF86keysyms.h
// Special keys used by Qtopia, mapped into the X11 private keypad range.
#define QTOPIAXK_Select 0x11000601
#define QTOPIAXK_Yes 0x11000602
#define QTOPIAXK_No 0x11000603
#define QTOPIAXK_Cancel 0x11000604
#define QTOPIAXK_Printer 0x11000605
#define QTOPIAXK_Execute 0x11000606
#define QTOPIAXK_Sleep 0x11000607
#define QTOPIAXK_Play 0x11000608
#define QTOPIAXK_Zoom 0x11000609
#define QTOPIAXK_Context1 0x1100060A
#define QTOPIAXK_Context2 0x1100060B
#define QTOPIAXK_Context3 0x1100060C
#define QTOPIAXK_Context4 0x1100060D
#define QTOPIAXK_Call 0x1100060E
#define QTOPIAXK_Hangup 0x1100060F
#define QTOPIAXK_Flip 0x11000610
// keyboard mapping table
static const unsigned int KeyTbl[] = {
// misc keys
XK_Escape, Qt::Key_Escape,
XK_Tab, Qt::Key_Tab,
XK_ISO_Left_Tab, Qt::Key_Backtab,
XK_BackSpace, Qt::Key_Backspace,
XK_Return, Qt::Key_Return,
XK_Insert, Qt::Key_Insert,
XK_Delete, Qt::Key_Delete,
XK_Clear, Qt::Key_Delete,
XK_Pause, Qt::Key_Pause,
XK_Print, Qt::Key_Print,
0x1005FF60, Qt::Key_SysReq, // hardcoded Sun SysReq
0x1007ff00, Qt::Key_SysReq, // hardcoded X386 SysReq
// cursor movement
XK_Home, Qt::Key_Home,
XK_End, Qt::Key_End,
XK_Left, Qt::Key_Left,
XK_Up, Qt::Key_Up,
XK_Right, Qt::Key_Right,
XK_Down, Qt::Key_Down,
XK_Prior, Qt::Key_PageUp,
XK_Next, Qt::Key_PageDown,
// modifiers
XK_Shift_L, Qt::Key_Shift,
XK_Shift_R, Qt::Key_Shift,
XK_Shift_Lock, Qt::Key_Shift,
XK_Control_L, Qt::Key_Control,
XK_Control_R, Qt::Key_Control,
XK_Meta_L, Qt::Key_Meta,
XK_Meta_R, Qt::Key_Meta,
XK_Alt_L, Qt::Key_Alt,
XK_Alt_R, Qt::Key_Alt,
XK_Caps_Lock, Qt::Key_CapsLock,
XK_Num_Lock, Qt::Key_NumLock,
XK_Scroll_Lock, Qt::Key_ScrollLock,
XK_Super_L, Qt::Key_Super_L,
XK_Super_R, Qt::Key_Super_R,
XK_Menu, Qt::Key_Menu,
XK_Hyper_L, Qt::Key_Hyper_L,
XK_Hyper_R, Qt::Key_Hyper_R,
XK_Help, Qt::Key_Help,
0x1000FF74, Qt::Key_Backtab, // hardcoded HP backtab
0x1005FF10, Qt::Key_F11, // hardcoded Sun F36 (labeled F11)
0x1005FF11, Qt::Key_F12, // hardcoded Sun F37 (labeled F12)
// numeric and function keypad keys
XK_KP_Enter, Qt::Key_Enter,
// special and additional keys
XK_Clear, Qt::Key_Clear,
XK_Delete, Qt::Key_Delete,
XK_space, Qt::Key_Space,
XK_exclam, Qt::Key_Exclam,
XK_quotedbl, Qt::Key_QuoteDbl,
XK_numbersign, Qt::Key_NumberSign,
XK_dollar, Qt::Key_Dollar,
XK_percent, Qt::Key_Percent,
XK_ampersand, Qt::Key_Ampersand,
XK_apostrophe, Qt::Key_Apostrophe,
XK_parenleft, Qt::Key_ParenLeft,
XK_parenright, Qt::Key_ParenRight,
XK_asterisk, Qt::Key_Asterisk,
XK_plus, Qt::Key_Plus,
XK_comma, Qt::Key_Comma,
XK_minus, Qt::Key_Minus,
XK_period, Qt::Key_Period,
XK_slash, Qt::Key_Slash,
XK_colon, Qt::Key_Colon,
XK_semicolon, Qt::Key_Semicolon,
XK_less, Qt::Key_Less,
XK_equal, Qt::Key_Equal,
XK_greater, Qt::Key_Greater,
XK_question, Qt::Key_Question,
XK_bracketleft, Qt::Key_BracketLeft,
XK_backslash, Qt::Key_Backslash,
XK_bracketright, Qt::Key_BracketRight,
XK_asciicircum, Qt::Key_AsciiCircum,
XK_underscore, Qt::Key_Underscore,
// International input method support keys
// International & multi-key character composition
XK_ISO_Level3_Shift, Qt::Key_AltGr,
XK_Multi_key, Qt::Key_Multi_key,
XK_Codeinput, Qt::Key_Codeinput,
XK_SingleCandidate, Qt::Key_SingleCandidate,
XK_MultipleCandidate, Qt::Key_MultipleCandidate,
XK_PreviousCandidate, Qt::Key_PreviousCandidate,
// Misc Functions
XK_Mode_switch, Qt::Key_Mode_switch,
XK_script_switch, Qt::Key_Mode_switch,
// Japanese keyboard support
XK_Kanji, Qt::Key_Kanji,
XK_Muhenkan, Qt::Key_Muhenkan,
//XK_Henkan_Mode, Qt::Key_Henkan_Mode,
XK_Henkan_Mode, Qt::Key_Henkan,
XK_Henkan, Qt::Key_Henkan,
XK_Romaji, Qt::Key_Romaji,
XK_Hiragana, Qt::Key_Hiragana,
XK_Katakana, Qt::Key_Katakana,
XK_Hiragana_Katakana, Qt::Key_Hiragana_Katakana,
XK_Zenkaku, Qt::Key_Zenkaku,
XK_Hankaku, Qt::Key_Hankaku,
XK_Zenkaku_Hankaku, Qt::Key_Zenkaku_Hankaku,
XK_Touroku, Qt::Key_Touroku,
XK_Massyo, Qt::Key_Massyo,
XK_Kana_Lock, Qt::Key_Kana_Lock,
XK_Kana_Shift, Qt::Key_Kana_Shift,
XK_Eisu_Shift, Qt::Key_Eisu_Shift,
XK_Eisu_toggle, Qt::Key_Eisu_toggle,
//XK_Kanji_Bangou, Qt::Key_Kanji_Bangou,
//XK_Zen_Koho, Qt::Key_Zen_Koho,
//XK_Mae_Koho, Qt::Key_Mae_Koho,
XK_Kanji_Bangou, Qt::Key_Codeinput,
XK_Zen_Koho, Qt::Key_MultipleCandidate,
XK_Mae_Koho, Qt::Key_PreviousCandidate,
#ifdef XK_KOREAN
// Korean keyboard support
XK_Hangul, Qt::Key_Hangul,
XK_Hangul_Start, Qt::Key_Hangul_Start,
XK_Hangul_End, Qt::Key_Hangul_End,
XK_Hangul_Hanja, Qt::Key_Hangul_Hanja,
XK_Hangul_Jamo, Qt::Key_Hangul_Jamo,
XK_Hangul_Romaja, Qt::Key_Hangul_Romaja,
//XK_Hangul_Codeinput, Qt::Key_Hangul_Codeinput,
XK_Hangul_Codeinput, Qt::Key_Codeinput,
XK_Hangul_Jeonja, Qt::Key_Hangul_Jeonja,
XK_Hangul_Banja, Qt::Key_Hangul_Banja,
XK_Hangul_PreHanja, Qt::Key_Hangul_PreHanja,
XK_Hangul_PostHanja, Qt::Key_Hangul_PostHanja,
//XK_Hangul_SingleCandidate,Qt::Key_Hangul_SingleCandidate,
//XK_Hangul_MultipleCandidate,Qt::Key_Hangul_MultipleCandidate,
//XK_Hangul_PreviousCandidate,Qt::Key_Hangul_PreviousCandidate,
XK_Hangul_SingleCandidate, Qt::Key_SingleCandidate,
XK_Hangul_MultipleCandidate,Qt::Key_MultipleCandidate,
XK_Hangul_PreviousCandidate,Qt::Key_PreviousCandidate,
XK_Hangul_Special, Qt::Key_Hangul_Special,
//XK_Hangul_switch, Qt::Key_Hangul_switch,
XK_Hangul_switch, Qt::Key_Mode_switch,
#endif // XK_KOREAN
// dead keys
XK_dead_grave, Qt::Key_Dead_Grave,
XK_dead_acute, Qt::Key_Dead_Acute,
XK_dead_circumflex, Qt::Key_Dead_Circumflex,
XK_dead_tilde, Qt::Key_Dead_Tilde,
XK_dead_macron, Qt::Key_Dead_Macron,
XK_dead_breve, Qt::Key_Dead_Breve,
XK_dead_abovedot, Qt::Key_Dead_Abovedot,
XK_dead_diaeresis, Qt::Key_Dead_Diaeresis,
XK_dead_abovering, Qt::Key_Dead_Abovering,
XK_dead_doubleacute, Qt::Key_Dead_Doubleacute,
XK_dead_caron, Qt::Key_Dead_Caron,
XK_dead_cedilla, Qt::Key_Dead_Cedilla,
XK_dead_ogonek, Qt::Key_Dead_Ogonek,
XK_dead_iota, Qt::Key_Dead_Iota,
XK_dead_voiced_sound, Qt::Key_Dead_Voiced_Sound,
XK_dead_semivoiced_sound, Qt::Key_Dead_Semivoiced_Sound,
XK_dead_belowdot, Qt::Key_Dead_Belowdot,
XK_dead_hook, Qt::Key_Dead_Hook,
XK_dead_horn, Qt::Key_Dead_Horn,
// Special multimedia keys
// currently only tested with MS internet keyboard
// browsing keys
XF86XK_Back, Qt::Key_Back,
XF86XK_Forward, Qt::Key_Forward,
XF86XK_Stop, Qt::Key_Stop,
XF86XK_Refresh, Qt::Key_Refresh,
XF86XK_Favorites, Qt::Key_Favorites,
XF86XK_AudioMedia, Qt::Key_LaunchMedia,
XF86XK_OpenURL, Qt::Key_OpenUrl,
XF86XK_HomePage, Qt::Key_HomePage,
XF86XK_Search, Qt::Key_Search,
// media keys
XF86XK_AudioLowerVolume, Qt::Key_VolumeDown,
XF86XK_AudioMute, Qt::Key_VolumeMute,
XF86XK_AudioRaiseVolume, Qt::Key_VolumeUp,
XF86XK_AudioPlay, Qt::Key_MediaPlay,
XF86XK_AudioStop, Qt::Key_MediaStop,
XF86XK_AudioPrev, Qt::Key_MediaPrevious,
XF86XK_AudioNext, Qt::Key_MediaNext,
XF86XK_AudioRecord, Qt::Key_MediaRecord,
// launch keys
XF86XK_Mail, Qt::Key_LaunchMail,
XF86XK_MyComputer, Qt::Key_Launch0,
XF86XK_Calculator, Qt::Key_Launch1,
XF86XK_Standby, Qt::Key_Standby,
XF86XK_Launch0, Qt::Key_Launch2,
XF86XK_Launch1, Qt::Key_Launch3,
XF86XK_Launch2, Qt::Key_Launch4,
XF86XK_Launch3, Qt::Key_Launch5,
XF86XK_Launch4, Qt::Key_Launch6,
XF86XK_Launch5, Qt::Key_Launch7,
XF86XK_Launch6, Qt::Key_Launch8,
XF86XK_Launch7, Qt::Key_Launch9,
XF86XK_Launch8, Qt::Key_LaunchA,
XF86XK_Launch9, Qt::Key_LaunchB,
XF86XK_LaunchA, Qt::Key_LaunchC,
XF86XK_LaunchB, Qt::Key_LaunchD,
XF86XK_LaunchC, Qt::Key_LaunchE,
XF86XK_LaunchD, Qt::Key_LaunchF,
// Qtopia keys
QTOPIAXK_Select, Qt::Key_Select,
QTOPIAXK_Yes, Qt::Key_Yes,
QTOPIAXK_No, Qt::Key_No,
QTOPIAXK_Cancel, Qt::Key_Cancel,
QTOPIAXK_Printer, Qt::Key_Printer,
QTOPIAXK_Execute, Qt::Key_Execute,
QTOPIAXK_Sleep, Qt::Key_Sleep,
QTOPIAXK_Play, Qt::Key_Play,
QTOPIAXK_Zoom, Qt::Key_Zoom,
QTOPIAXK_Context1, Qt::Key_Context1,
QTOPIAXK_Context2, Qt::Key_Context2,
QTOPIAXK_Context3, Qt::Key_Context3,
QTOPIAXK_Context4, Qt::Key_Context4,
QTOPIAXK_Call, Qt::Key_Call,
QTOPIAXK_Hangup, Qt::Key_Hangup,
QTOPIAXK_Flip, Qt::Key_Flip,
0, 0
};
#endif // KEYMAPPER_X11_H

View File

@@ -1,251 +0,0 @@
/****************************************************************************
** Copyright (c) 2006 - 2011, the LibQxt project.
** See the Qxt AUTHORS file for a list of authors and copyright holders.
** All rights reserved.
**
** Redistribution and use in source and binary forms, with or without
** modification, are permitted provided that the following conditions are met:
** * Redistributions of source code must retain the above copyright
** notice, this list of conditions and the following disclaimer.
** * Redistributions in binary form must reproduce the above copyright
** notice, this list of conditions and the following disclaimer in the
** documentation and/or other materials provided with the distribution.
** * Neither the name of the LibQxt project 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 <COPYRIGHT HOLDER> 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.
**
** <http://libqxt.org> <foundation@libqxt.org>
*****************************************************************************/
#include "qxtglobal.h"
/*!
\headerfile <QxtGlobal>
\title Global Qxt Declarations
\inmodule QxtCore
\brief The <QxtGlobal> header provides basic declarations and
is included by all other Qxt headers.
*/
/*!
\macro QXT_VERSION
\relates <QxtGlobal>
This macro expands a numeric value of the form 0xMMNNPP (MM =
major, NN = minor, PP = patch) that specifies Qxt's version
number. For example, if you compile your application against Qxt
0.4.0, the QXT_VERSION macro will expand to 0x000400.
You can use QXT_VERSION to use the latest Qt features where
available. For example:
\code
#if QXT_VERSION >= 0x000400
qxtTabWidget->setTabMovementMode(QxtTabWidget::InPlaceMovement);
#endif
\endcode
\sa QXT_VERSION_STR, qxtVersion()
*/
/*!
\macro QXT_VERSION_STR
\relates <QxtGlobal>
This macro expands to a string that specifies Qxt's version number
(for example, "0.4.0"). This is the version against which the
application is compiled.
\sa qxtVersion(), QXT_VERSION
*/
/*!
\relates <QxtGlobal>
Returns the version number of Qxt at run-time as a string (for
example, "0.4.0"). This may be a different version than the
version the application was compiled against.
\sa QXT_VERSION_STR
*/
const char * qxtVersion()
{
return QXT_VERSION_STR;
}
/*!
\headerfile <QxtPimpl>
\title The Qxt private implementation
\inmodule QxtCore
\brief The <QxtPimpl> header provides tools for hiding
details of a class.
Application code generally doesn't have to be concerned about hiding its
implementation details, but when writing library code it is important to
maintain a constant interface, both source and binary. Maintaining a constant
source interface is easy enough, but keeping the binary interface constant
means moving implementation details into a private class. The PIMPL, or
d-pointer, idiom is a common method of implementing this separation. QxtPimpl
offers a convenient way to connect the public and private sides of your class.
\section1 Getting Started
Before you declare the public class, you need to make a forward declaration
of the private class. The private class must have the same name as the public
class, followed by the word Private. For example, a class named MyTest would
declare the private class with:
\code
class MyTestPrivate;
\endcode
\section1 The Public Class
Generally, you shouldn't keep any data members in the public class without a
good reason. Functions that are part of the public interface should be declared
in the public class, and functions that need to be available to subclasses (for
calling or overriding) should be in the protected section of the public class.
To connect the private class to the public class, include the
QXT_DECLARE_PRIVATE macro in the private section of the public class. In the
example above, the private class is connected as follows:
\code
private:
QXT_DECLARE_PRIVATE(MyTest)
\endcode
Additionally, you must include the QXT_INIT_PRIVATE macro in the public class's
constructor. Continuing with the MyTest example, your constructor might look
like this:
\code
MyTest::MyTest() {
// initialization
QXT_INIT_PRIVATE(MyTest);
}
\endcode
\section1 The Private Class
As mentioned above, data members should usually be kept in the private class.
This allows the memory layout of the private class to change without breaking
binary compatibility for the public class. Functions that exist only as
implementation details, or functions that need access to private data members,
should be implemented here.
To define the private class, inherit from the template QxtPrivate class, and
include the QXT_DECLARE_PUBLIC macro in its public section. The template
parameter should be the name of the public class. For example:
\code
class MyTestPrivate : public QxtPrivate<MyTest> {
public:
MyTestPrivate();
QXT_DECLARE_PUBLIC(MyTest)
};
\endcode
\section1 Accessing Private Members
Use the qxt_d() function (actually a function-like object) from functions in
the public class to access the private class. Similarly, functions in the
private class can invoke functions in the public class by using the qxt_p()
function (this one's actually a function).
For example, assume that MyTest has methods named getFoobar and doBaz(),
and MyTestPrivate has a member named foobar and a method named doQuux().
The code might resemble this example:
\code
int MyTest::getFoobar() {
return qxt_d().foobar;
}
void MyTestPrivate::doQuux() {
qxt_p().doBaz(foobar);
}
\endcode
*/
/*!
* \macro QXT_DECLARE_PRIVATE(PUB)
* \relates <QxtPimpl>
* Declares that a public class has a related private class.
*
* This shuold be put in the private section of the public class. The
* parameter \a PUB must be the name of the public class.
*/
/*!
* \macro QXT_DECLARE_PUBLIC(PUB)
* \relates <QxtPimpl>
* Declares that a private class has a related public class named \a PUB.
*
* This may be put anywhere in the declaration of the private class. The parameter is the name of the public class.
*/
/*!
* \macro QXT_INIT_PRIVATE(PUB)
* \relates <QxtPimpl>
* Initializes resources owned by the private class.
*
* This should be called from the public class's constructor,
* before qxt_d() is used for the first time. The parameter \a PUB must be
* the name of the public class.
*/
/*!
* \macro QXT_D(PUB)
* \relates <QxtPimpl>
* Returns a reference in the current scope named "d" to the private class
* associated with the public class \a PUB.
*
* This function is only available in a class using QXT_DECLARE_PRIVATE().
*/
/*!
* \macro QXT_P(PUB)
* \relates <QxtPimpl>
* Creates a reference in the current scope named "q" to the public class
* named \a PUB.
*
* This macro only works in a class using QXT_DECLARE_PUBLIC().
*/
/*!
* \fn QxtPrivate<PUB>& PUB::qxt_d()
* \relates <QxtPimpl>
* Returns a reference to the private class.
*
* This function is only available in a class using \a QXT_DECLARE_PRIVATE.
*/
/*!
* \fn const QxtPrivate<PUB>& PUB::qxt_d() const
* \relates <QxtPimpl>
* Returns a const reference to the private class.
*
* This function is only available in a class using \a QXT_DECLARE_PRIVATE.
* This overload will be automatically used in const functions.
*/
/*!
* \fn PUB& QxtPrivate::qxt_p()
* \relates <QxtPimpl>
* Returns a reference to the public class.
*
* This function is only available in a class using QXT_DECLARE_PUBLIC().
*/
/*!
* \fn const PUB& QxtPrivate::qxt_p() const
* \relates <QxtPimpl>
* Returns a const reference to the public class.
*
* This function is only available in a class using QXT_DECLARE_PUBLIC().
* This overload will be automatically used in const functions.
*/

View File

@@ -1,233 +0,0 @@
/****************************************************************************
** Copyright (c) 2006 - 2011, the LibQxt project.
** See the Qxt AUTHORS file for a list of authors and copyright holders.
** All rights reserved.
**
** Redistribution and use in source and binary forms, with or without
** modification, are permitted provided that the following conditions are met:
** * Redistributions of source code must retain the above copyright
** notice, this list of conditions and the following disclaimer.
** * Redistributions in binary form must reproduce the above copyright
** notice, this list of conditions and the following disclaimer in the
** documentation and/or other materials provided with the distribution.
** * Neither the name of the LibQxt project 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 <COPYRIGHT HOLDER> 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.
**
** <http://libqxt.org> <foundation@libqxt.org>
*****************************************************************************/
#ifndef QXTGLOBAL_H
#define QXTGLOBAL_H
#include <QtGlobal>
#define QXT_VERSION 0x000700
#define QXT_VERSION_STR "0.7.0"
//--------------------------global macros------------------------------
#ifndef QXT_NO_MACROS
#ifndef _countof
#define _countof(x) (sizeof(x)/sizeof(*x))
#endif
#endif // QXT_NO_MACROS
//--------------------------export macros------------------------------
#define QXT_DLLEXPORT DO_NOT_USE_THIS_ANYMORE
#if !defined(QXT_STATIC) && !defined(QXT_DOXYGEN_RUN)
# if defined(BUILD_QXT_CORE)
# define QXT_CORE_EXPORT Q_DECL_EXPORT
# else
# define QXT_CORE_EXPORT Q_DECL_IMPORT
# endif
#else
# define QXT_CORE_EXPORT
#endif // BUILD_QXT_CORE
#if !defined(QXT_STATIC) && !defined(QXT_DOXYGEN_RUN)
# if defined(BUILD_QXT_GUI)
# define QXT_GUI_EXPORT Q_DECL_EXPORT
# else
# define QXT_GUI_EXPORT Q_DECL_IMPORT
# endif
#else
# define QXT_GUI_EXPORT
#endif // BUILD_QXT_GUI
#if !defined(QXT_STATIC) && !defined(QXT_DOXYGEN_RUN)
# if defined(BUILD_QXT_NETWORK)
# define QXT_NETWORK_EXPORT Q_DECL_EXPORT
# else
# define QXT_NETWORK_EXPORT Q_DECL_IMPORT
# endif
#else
# define QXT_NETWORK_EXPORT
#endif // BUILD_QXT_NETWORK
#if !defined(QXT_STATIC) && !defined(QXT_DOXYGEN_RUN)
# if defined(BUILD_QXT_SQL)
# define QXT_SQL_EXPORT Q_DECL_EXPORT
# else
# define QXT_SQL_EXPORT Q_DECL_IMPORT
# endif
#else
# define QXT_SQL_EXPORT
#endif // BUILD_QXT_SQL
#if !defined(QXT_STATIC) && !defined(QXT_DOXYGEN_RUN)
# if defined(BUILD_QXT_WEB)
# define QXT_WEB_EXPORT Q_DECL_EXPORT
# else
# define QXT_WEB_EXPORT Q_DECL_IMPORT
# endif
#else
# define QXT_WEB_EXPORT
#endif // BUILD_QXT_WEB
#if !defined(QXT_STATIC) && !defined(QXT_DOXYGEN_RUN)
# if defined(BUILD_QXT_BERKELEY)
# define QXT_BERKELEY_EXPORT Q_DECL_EXPORT
# else
# define QXT_BERKELEY_EXPORT Q_DECL_IMPORT
# endif
#else
# define QXT_BERKELEY_EXPORT
#endif // BUILD_QXT_BERKELEY
#if !defined(QXT_STATIC) && !defined(QXT_DOXYGEN_RUN)
# if defined(BUILD_QXT_ZEROCONF)
# define QXT_ZEROCONF_EXPORT Q_DECL_EXPORT
# else
# define QXT_ZEROCONF_EXPORT Q_DECL_IMPORT
# endif
#else
# define QXT_ZEROCONF_EXPORT
#endif // QXT_ZEROCONF_EXPORT
#if defined(BUILD_QXT_CORE) || defined(BUILD_QXT_GUI) || defined(BUILD_QXT_SQL) || defined(BUILD_QXT_NETWORK) || defined(BUILD_QXT_WEB) || defined(BUILD_QXT_BERKELEY) || defined(BUILD_QXT_ZEROCONF)
# define BUILD_QXT
#endif
QXT_CORE_EXPORT const char* qxtVersion();
#ifndef QT_BEGIN_NAMESPACE
#define QT_BEGIN_NAMESPACE
#endif
#ifndef QT_END_NAMESPACE
#define QT_END_NAMESPACE
#endif
#ifndef QT_FORWARD_DECLARE_CLASS
#define QT_FORWARD_DECLARE_CLASS(Class) class Class;
#endif
/****************************************************************************
** This file is derived from code bearing the following notice:
** The sole author of this file, Adam Higerd, has explicitly disclaimed all
** copyright interest and protection for the content within. This file has
** been placed in the public domain according to United States copyright
** statute and case law. In jurisdictions where this public domain dedication
** is not legally recognized, anyone who receives a copy of this file is
** permitted to use, modify, duplicate, and redistribute this file, in whole
** or in part, with no restrictions or conditions. In these jurisdictions,
** this file shall be copyright (C) 2006-2008 by Adam Higerd.
****************************************************************************/
#define QXT_DECLARE_PRIVATE(PUB) friend class PUB##Private; QxtPrivateInterface<PUB, PUB##Private> qxt_d;
#define QXT_DECLARE_PUBLIC(PUB) friend class PUB;
#define QXT_INIT_PRIVATE(PUB) qxt_d.setPublic(this);
#define QXT_D(PUB) PUB##Private& d = qxt_d()
#define QXT_P(PUB) PUB& p = qxt_p()
template <typename PUB>
class QxtPrivate
{
public:
virtual ~QxtPrivate()
{}
inline void QXT_setPublic(PUB* pub)
{
qxt_p_ptr = pub;
}
protected:
inline PUB& qxt_p()
{
return *qxt_p_ptr;
}
inline const PUB& qxt_p() const
{
return *qxt_p_ptr;
}
inline PUB* qxt_ptr()
{
return qxt_p_ptr;
}
inline const PUB* qxt_ptr() const
{
return qxt_p_ptr;
}
private:
PUB* qxt_p_ptr;
};
template <typename PUB, typename PVT>
class QxtPrivateInterface
{
friend class QxtPrivate<PUB>;
public:
QxtPrivateInterface()
{
pvt = new PVT;
}
~QxtPrivateInterface()
{
delete pvt;
}
inline void setPublic(PUB* pub)
{
pvt->QXT_setPublic(pub);
}
inline PVT& operator()()
{
return *static_cast<PVT*>(pvt);
}
inline const PVT& operator()() const
{
return *static_cast<PVT*>(pvt);
}
inline PVT * operator->()
{
return static_cast<PVT*>(pvt);
}
inline const PVT * operator->() const
{
return static_cast<PVT*>(pvt);
}
private:
QxtPrivateInterface(const QxtPrivateInterface&) { }
QxtPrivateInterface& operator=(const QxtPrivateInterface&) { }
QxtPrivate<PUB>* pvt;
};
#endif // QXT_GLOBAL

View File

@@ -1,222 +0,0 @@
#include "qxtglobalshortcut.h"
/****************************************************************************
** Copyright (c) 2006 - 2011, the LibQxt project.
** See the Qxt AUTHORS file for a list of authors and copyright holders.
** All rights reserved.
**
** Redistribution and use in source and binary forms, with or without
** modification, are permitted provided that the following conditions are met:
** * Redistributions of source code must retain the above copyright
** notice, this list of conditions and the following disclaimer.
** * Redistributions in binary form must reproduce the above copyright
** notice, this list of conditions and the following disclaimer in the
** documentation and/or other materials provided with the distribution.
** * Neither the name of the LibQxt project 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 <COPYRIGHT HOLDER> 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.
**
** <http://libqxt.org> <foundation@libqxt.org>
*****************************************************************************/
#include <QtGlobal>
#include <QAbstractEventDispatcher>
#include <QHash>
#include <QPair>
#include <QtDebug>
#include "qxtglobalshortcut_p.h"
bool QxtGlobalShortcutPrivate::error = false;
#ifndef Q_WS_MAC
int QxtGlobalShortcutPrivate::ref = 0;
#if QT_VERSION < QT_VERSION_CHECK(5,0,0)
QAbstractEventDispatcher::EventFilter QxtGlobalShortcutPrivate::prevEventFilter = 0;
#endif
#endif // Q_WS_MAC
QHash<QPair<quint32, quint32>, QxtGlobalShortcut*> QxtGlobalShortcutPrivate::shortcuts;
QxtGlobalShortcutPrivate::QxtGlobalShortcutPrivate() : enabled(true), key(Qt::Key(0)), mods(Qt::NoModifier)
{
#ifndef Q_WS_MAC
if (!ref++)
#if QT_VERSION < QT_VERSION_CHECK(5,0,0)
prevEventFilter = QAbstractEventDispatcher::instance()->setEventFilter(eventFilter);
#else
QAbstractEventDispatcher::instance()->installNativeEventFilter(this);
#endif
#endif // Q_WS_MAC
}
QxtGlobalShortcutPrivate::~QxtGlobalShortcutPrivate()
{
#ifndef Q_WS_MAC
if (!--ref)
#if QT_VERSION < QT_VERSION_CHECK(5,0,0)
QAbstractEventDispatcher::instance()->setEventFilter(prevEventFilter);
#else
QAbstractEventDispatcher::instance()->removeNativeEventFilter(this);
#endif
#endif // Q_WS_MAC
}
bool QxtGlobalShortcutPrivate::setShortcut(const QKeySequence& shortcut)
{
Qt::KeyboardModifiers allMods = Qt::ShiftModifier | Qt::ControlModifier | Qt::AltModifier | Qt::MetaModifier;
key = shortcut.isEmpty() ? Qt::Key(0) : Qt::Key((shortcut[0] ^ allMods) & shortcut[0]);
mods = shortcut.isEmpty() ? Qt::KeyboardModifiers(0) : Qt::KeyboardModifiers(shortcut[0] & allMods);
const quint32 nativeKey = nativeKeycode(key);
const quint32 nativeMods = nativeModifiers(mods);
const bool res = registerShortcut(nativeKey, nativeMods);
if (res)
shortcuts.insert(qMakePair(nativeKey, nativeMods), &qxt_p());
else
qWarning() << "QxtGlobalShortcut failed to register:" << QKeySequence(key + mods).toString();
return res;
}
bool QxtGlobalShortcutPrivate::unsetShortcut()
{
bool res = false;
const quint32 nativeKey = nativeKeycode(key);
const quint32 nativeMods = nativeModifiers(mods);
if (shortcuts.value(qMakePair(nativeKey, nativeMods)) == &qxt_p())
res = unregisterShortcut(nativeKey, nativeMods);
if (res)
shortcuts.remove(qMakePair(nativeKey, nativeMods));
else
qWarning() << "QxtGlobalShortcut failed to unregister:" << QKeySequence(key + mods).toString();
key = Qt::Key(0);
mods = Qt::KeyboardModifiers(0);
return res;
}
void QxtGlobalShortcutPrivate::activateShortcut(quint32 nativeKey, quint32 nativeMods)
{
QxtGlobalShortcut* shortcut = shortcuts.value(qMakePair(nativeKey, nativeMods));
if (shortcut && shortcut->isEnabled())
emit shortcut->activated();
}
/*!
\class QxtGlobalShortcut
\inmodule QxtWidgets
\brief The QxtGlobalShortcut class provides a global shortcut aka "hotkey".
A global shortcut triggers even if the application is not active. This
makes it easy to implement applications that react to certain shortcuts
still if some other application is active or if the application is for
example minimized to the system tray.
Example usage:
\code
QxtGlobalShortcut* shortcut = new QxtGlobalShortcut(window);
connect(shortcut, SIGNAL(activated()), window, SLOT(toggleVisibility()));
shortcut->setShortcut(QKeySequence("Ctrl+Shift+F12"));
\endcode
\bold {Note:} Since Qxt 0.6 QxtGlobalShortcut no more requires QxtApplication.
*/
/*!
\fn QxtGlobalShortcut::activated()
This signal is emitted when the user types the shortcut's key sequence.
\sa shortcut
*/
/*!
Constructs a new QxtGlobalShortcut with \a parent.
*/
QxtGlobalShortcut::QxtGlobalShortcut(QObject* parent)
: QObject(parent)
{
QXT_INIT_PRIVATE(QxtGlobalShortcut);
}
/*!
Constructs a new QxtGlobalShortcut with \a shortcut and \a parent.
*/
QxtGlobalShortcut::QxtGlobalShortcut(const QKeySequence& shortcut, QObject* parent)
: QObject(parent)
{
QXT_INIT_PRIVATE(QxtGlobalShortcut);
setShortcut(shortcut);
}
/*!
Destructs the QxtGlobalShortcut.
*/
QxtGlobalShortcut::~QxtGlobalShortcut()
{
if (qxt_d().key != 0)
qxt_d().unsetShortcut();
}
/*!
\property QxtGlobalShortcut::shortcut
\brief the shortcut key sequence
\bold {Note:} Notice that corresponding key press and release events are not
delivered for registered global shortcuts even if they are disabled.
Also, comma separated key sequences are not supported.
Only the first part is used:
\code
qxtShortcut->setShortcut(QKeySequence("Ctrl+Alt+A,Ctrl+Alt+B"));
Q_ASSERT(qxtShortcut->shortcut() == QKeySequence("Ctrl+Alt+A"));
\endcode
*/
QKeySequence QxtGlobalShortcut::shortcut() const
{
return QKeySequence(qxt_d().key | qxt_d().mods);
}
bool QxtGlobalShortcut::setShortcut(const QKeySequence& shortcut)
{
if (qxt_d().key != 0)
qxt_d().unsetShortcut();
return qxt_d().setShortcut(shortcut);
}
/*!
\property QxtGlobalShortcut::enabled
\brief whether the shortcut is enabled
A disabled shortcut does not get activated.
The default value is \c true.
\sa setDisabled()
*/
bool QxtGlobalShortcut::isEnabled() const
{
return qxt_d().enabled;
}
void QxtGlobalShortcut::setEnabled(bool enabled)
{
qxt_d().enabled = enabled;
}
/*!
Sets the shortcut \a disabled.
\sa enabled
*/
void QxtGlobalShortcut::setDisabled(bool disabled)
{
qxt_d().enabled = !disabled;
}

View File

@@ -1,68 +0,0 @@
#ifndef QXTGLOBALSHORTCUT_H
/****************************************************************************
** Copyright (c) 2006 - 2011, the LibQxt project.
** See the Qxt AUTHORS file for a list of authors and copyright holders.
** All rights reserved.
**
** Redistribution and use in source and binary forms, with or without
** modification, are permitted provided that the following conditions are met:
** * Redistributions of source code must retain the above copyright
** notice, this list of conditions and the following disclaimer.
** * Redistributions in binary form must reproduce the above copyright
** notice, this list of conditions and the following disclaimer in the
** documentation and/or other materials provided with the distribution.
** * Neither the name of the LibQxt project 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 <COPYRIGHT HOLDER> 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.
**
** <http://libqxt.org> <foundation@libqxt.org>
*****************************************************************************/
#define QXTGLOBALSHORTCUT_H
#include <QObject>
#include <QKeySequence>
#include <QString>
#include "qxtglobal.h"
class QxtGlobalShortcutPrivate;
class QXT_GUI_EXPORT QxtGlobalShortcut : public QObject
{
Q_OBJECT
QXT_DECLARE_PRIVATE(QxtGlobalShortcut)
Q_PROPERTY(bool enabled READ isEnabled WRITE setEnabled)
Q_PROPERTY(QKeySequence shortcut READ shortcut WRITE setShortcut)
public:
explicit QxtGlobalShortcut(QObject* parent = 0);
explicit QxtGlobalShortcut(const QKeySequence& shortcut, QObject* parent = 0);
virtual ~QxtGlobalShortcut();
QKeySequence shortcut() const;
bool setShortcut(const QKeySequence& shortcut);
bool isEnabled() const;
public Q_SLOTS:
void setEnabled(bool enabled = true);
void setDisabled(bool disabled = true);
Q_SIGNALS:
void activated();
};
#endif // QXTGLOBALSHORTCUT_H

View File

@@ -1,258 +0,0 @@
#include <Carbon/Carbon.h>
/****************************************************************************
** Copyright (c) 2006 - 2011, the LibQxt project.
** See the Qxt AUTHORS file for a list of authors and copyright holders.
** All rights reserved.
**
** Redistribution and use in source and binary forms, with or without
** modification, are permitted provided that the following conditions are met:
** * Redistributions of source code must retain the above copyright
** notice, this list of conditions and the following disclaimer.
** * Redistributions in binary form must reproduce the above copyright
** notice, this list of conditions and the following disclaimer in the
** documentation and/or other materials provided with the distribution.
** * Neither the name of the LibQxt project 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 <COPYRIGHT HOLDER> 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.
**
** <http://libqxt.org> <foundation@libqxt.org>
*****************************************************************************/
#include "qxtglobalshortcut_p.h"
#include <QMap>
#include <QHash>
#include <QtDebug>
#include <QApplication>
typedef QPair<uint, uint> Identifier;
static QMap<quint32, EventHotKeyRef> keyRefs;
static QHash<Identifier, quint32> keyIDs;
static quint32 hotKeySerial = 0;
static bool qxt_mac_handler_installed = false;
OSStatus qxt_mac_handle_hot_key(EventHandlerCallRef nextHandler, EventRef event, void* data)
{
Q_UNUSED(nextHandler);
Q_UNUSED(data);
if (GetEventClass(event) == kEventClassKeyboard && GetEventKind(event) == kEventHotKeyPressed)
{
EventHotKeyID keyID;
GetEventParameter(event, kEventParamDirectObject, typeEventHotKeyID, NULL, sizeof(keyID), NULL, &keyID);
Identifier id = keyIDs.key(keyID.id);
QxtGlobalShortcutPrivate::activateShortcut(id.second, id.first);
}
return noErr;
}
quint32 QxtGlobalShortcutPrivate::nativeModifiers(Qt::KeyboardModifiers modifiers)
{
quint32 native = 0;
if (modifiers & Qt::ShiftModifier)
native |= shiftKey;
if (modifiers & Qt::ControlModifier)
native |= cmdKey;
if (modifiers & Qt::AltModifier)
native |= optionKey;
if (modifiers & Qt::MetaModifier)
native |= controlKey;
if (modifiers & Qt::KeypadModifier)
native |= kEventKeyModifierNumLockMask;
return native;
}
quint32 QxtGlobalShortcutPrivate::nativeKeycode(Qt::Key key)
{
UTF16Char ch;
// Constants found in NSEvent.h from AppKit.framework
switch (key)
{
case Qt::Key_Return:
return kVK_Return;
case Qt::Key_Enter:
return kVK_ANSI_KeypadEnter;
case Qt::Key_Tab:
return kVK_Tab;
case Qt::Key_Space:
return kVK_Space;
case Qt::Key_Backspace:
return kVK_Delete;
case Qt::Key_Control:
return kVK_Command;
case Qt::Key_Shift:
return kVK_Shift;
case Qt::Key_CapsLock:
return kVK_CapsLock;
case Qt::Key_Option:
return kVK_Option;
case Qt::Key_Meta:
return kVK_Control;
case Qt::Key_F17:
return kVK_F17;
case Qt::Key_VolumeUp:
return kVK_VolumeUp;
case Qt::Key_VolumeDown:
return kVK_VolumeDown;
case Qt::Key_F18:
return kVK_F18;
case Qt::Key_F19:
return kVK_F19;
case Qt::Key_F20:
return kVK_F20;
case Qt::Key_F5:
return kVK_F5;
case Qt::Key_F6:
return kVK_F6;
case Qt::Key_F7:
return kVK_F7;
case Qt::Key_F3:
return kVK_F3;
case Qt::Key_F8:
return kVK_F8;
case Qt::Key_F9:
return kVK_F9;
case Qt::Key_F11:
return kVK_F11;
case Qt::Key_F13:
return kVK_F13;
case Qt::Key_F16:
return kVK_F16;
case Qt::Key_F14:
return kVK_F14;
case Qt::Key_F10:
return kVK_F10;
case Qt::Key_F12:
return kVK_F12;
case Qt::Key_F15:
return kVK_F15;
case Qt::Key_Help:
return kVK_Help;
case Qt::Key_Home:
return kVK_Home;
case Qt::Key_PageUp:
return kVK_PageUp;
case Qt::Key_Delete:
return kVK_ForwardDelete;
case Qt::Key_F4:
return kVK_F4;
case Qt::Key_End:
return kVK_End;
case Qt::Key_F2:
return kVK_F2;
case Qt::Key_PageDown:
return kVK_PageDown;
case Qt::Key_F1:
return kVK_F1;
case Qt::Key_Left:
return kVK_LeftArrow;
case Qt::Key_Right:
return kVK_RightArrow;
case Qt::Key_Down:
return kVK_DownArrow;
case Qt::Key_Up:
return kVK_UpArrow;
default:
;
}
if (key == Qt::Key_Escape) ch = 27;
else if (key == Qt::Key_Return) ch = 13;
else if (key == Qt::Key_Enter) ch = 3;
else if (key == Qt::Key_Tab) ch = 9;
else ch = key;
CFDataRef currentLayoutData;
TISInputSourceRef currentKeyboard = TISCopyCurrentKeyboardInputSource();
if (currentKeyboard == NULL)
return 0;
currentLayoutData = (CFDataRef)TISGetInputSourceProperty(currentKeyboard, kTISPropertyUnicodeKeyLayoutData);
CFRelease(currentKeyboard);
if (currentLayoutData == NULL)
return 0;
UCKeyboardLayout* header = (UCKeyboardLayout*)CFDataGetBytePtr(currentLayoutData);
UCKeyboardTypeHeader* table = header->keyboardTypeList;
uint8_t *data = (uint8_t*)header;
// God, would a little documentation for this shit kill you...
for (quint32 i=0; i < header->keyboardTypeCount; i++)
{
UCKeyStateRecordsIndex* stateRec = 0;
if (table[i].keyStateRecordsIndexOffset != 0)
{
stateRec = reinterpret_cast<UCKeyStateRecordsIndex*>(data + table[i].keyStateRecordsIndexOffset);
if (stateRec->keyStateRecordsIndexFormat != kUCKeyStateRecordsIndexFormat) stateRec = 0;
}
UCKeyToCharTableIndex* charTable = reinterpret_cast<UCKeyToCharTableIndex*>(data + table[i].keyToCharTableIndexOffset);
if (charTable->keyToCharTableIndexFormat != kUCKeyToCharTableIndexFormat) continue;
for (quint32 j=0; j < charTable->keyToCharTableCount; j++)
{
UCKeyOutput* keyToChar = reinterpret_cast<UCKeyOutput*>(data + charTable->keyToCharTableOffsets[j]);
for (quint32 k=0; k < charTable->keyToCharTableSize; k++)
{
if (keyToChar[k] & kUCKeyOutputTestForIndexMask)
{
long idx = keyToChar[k] & kUCKeyOutputGetIndexMask;
if (stateRec && idx < stateRec->keyStateRecordCount)
{
UCKeyStateRecord* rec = reinterpret_cast<UCKeyStateRecord*>(data + stateRec->keyStateRecordOffsets[idx]);
if (rec->stateZeroCharData == ch) return k;
}
}
else if (!(keyToChar[k] & kUCKeyOutputSequenceIndexMask) && keyToChar[k] < 0xFFFE)
{
if (keyToChar[k] == ch) return k;
}
} // for k
} // for j
} // for i
return 0;
}
bool QxtGlobalShortcutPrivate::registerShortcut(quint32 nativeKey, quint32 nativeMods)
{
if (!qxt_mac_handler_installed)
{
EventTypeSpec t;
t.eventClass = kEventClassKeyboard;
t.eventKind = kEventHotKeyPressed;
InstallApplicationEventHandler(&qxt_mac_handle_hot_key, 1, &t, NULL, NULL);
}
EventHotKeyID keyID;
keyID.signature = 'cute';
keyID.id = ++hotKeySerial;
EventHotKeyRef ref = 0;
bool rv = !RegisterEventHotKey(nativeKey, nativeMods, keyID, GetApplicationEventTarget(), 0, &ref);
if (rv)
{
keyIDs.insert(Identifier(nativeMods, nativeKey), keyID.id);
keyRefs.insert(keyID.id, ref);
}
return rv;
}
bool QxtGlobalShortcutPrivate::unregisterShortcut(quint32 nativeKey, quint32 nativeMods)
{
Identifier id(nativeMods, nativeKey);
if (!keyIDs.contains(id)) return false;
EventHotKeyRef ref = keyRefs.take(keyIDs[id]);
keyIDs.remove(id);
return !UnregisterEventHotKey(ref);
}

View File

@@ -1,84 +0,0 @@
#ifndef QXTGLOBALSHORTCUT_P_H
/****************************************************************************
** Copyright (c) 2006 - 2011, the LibQxt project.
** See the Qxt AUTHORS file for a list of authors and copyright holders.
** All rights reserved.
**
** Redistribution and use in source and binary forms, with or without
** modification, are permitted provided that the following conditions are met:
** * Redistributions of source code must retain the above copyright
** notice, this list of conditions and the following disclaimer.
** * Redistributions in binary form must reproduce the above copyright
** notice, this list of conditions and the following disclaimer in the
** documentation and/or other materials provided with the distribution.
** * Neither the name of the LibQxt project 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 <COPYRIGHT HOLDER> 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.
**
** <http://libqxt.org> <foundation@libqxt.org>
*****************************************************************************/
#define QXTGLOBALSHORTCUT_P_H
#include "qxtglobalshortcut.h"
#include <QAbstractEventDispatcher>
#include <QKeySequence>
#include <QHash>
#if QT_VERSION >= QT_VERSION_CHECK(5,0,0)
#include <QAbstractNativeEventFilter>
#endif
class QxtGlobalShortcutPrivate : public QxtPrivate<QxtGlobalShortcut>
#if QT_VERSION >= QT_VERSION_CHECK(5,0,0)
,public QAbstractNativeEventFilter
#endif
{
public:
QXT_DECLARE_PUBLIC(QxtGlobalShortcut)
QxtGlobalShortcutPrivate();
~QxtGlobalShortcutPrivate();
bool enabled;
Qt::Key key;
Qt::KeyboardModifiers mods;
bool setShortcut(const QKeySequence& shortcut);
bool unsetShortcut();
static bool error;
#ifndef Q_WS_MAC
static int ref;
#if QT_VERSION < QT_VERSION_CHECK(5,0,0)
static QAbstractEventDispatcher::EventFilter prevEventFilter;
static bool eventFilter(void* message);
#else
virtual bool nativeEventFilter(const QByteArray & eventType, void * message, long * result);
#endif // QT_VERSION < QT_VERSION_CHECK(5,0,0)
#endif // Q_WS_MAC
static void activateShortcut(quint32 nativeKey, quint32 nativeMods);
private:
static quint32 nativeKeycode(Qt::Key keycode);
static quint32 nativeModifiers(Qt::KeyboardModifiers modifiers);
static bool registerShortcut(quint32 nativeKey, quint32 nativeMods);
static bool unregisterShortcut(quint32 nativeKey, quint32 nativeMods);
static QHash<QPair<quint32, quint32>, QxtGlobalShortcut*> shortcuts;
};
#endif // QXTGLOBALSHORTCUT_P_H

View File

@@ -1,247 +0,0 @@
#include "qxtglobalshortcut_p.h"
/****************************************************************************
** Copyright (c) 2006 - 2011, the LibQxt project.
** See the Qxt AUTHORS file for a list of authors and copyright holders.
** All rights reserved.
**
** Redistribution and use in source and binary forms, with or without
** modification, are permitted provided that the following conditions are met:
** * Redistributions of source code must retain the above copyright
** notice, this list of conditions and the following disclaimer.
** * Redistributions in binary form must reproduce the above copyright
** notice, this list of conditions and the following disclaimer in the
** documentation and/or other materials provided with the distribution.
** * Neither the name of the LibQxt project 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 <COPYRIGHT HOLDER> 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.
**
** <http://libqxt.org> <foundation@libqxt.org>
*****************************************************************************/
#include <qt_windows.h>
#if QT_VERSION < QT_VERSION_CHECK(5,0,0)
bool QxtGlobalShortcutPrivate::eventFilter(void* message)
{
#else
bool QxtGlobalShortcutPrivate::nativeEventFilter(const QByteArray & eventType,
void * message, long * result)
{
Q_UNUSED(eventType);
Q_UNUSED(result);
#endif
MSG* msg = static_cast<MSG*>(message);
if (msg->message == WM_HOTKEY)
{
const quint32 keycode = HIWORD(msg->lParam);
const quint32 modifiers = LOWORD(msg->lParam);
activateShortcut(keycode, modifiers);
}
return false;
}
quint32 QxtGlobalShortcutPrivate::nativeModifiers(Qt::KeyboardModifiers modifiers)
{
// MOD_ALT, MOD_CONTROL, (MOD_KEYUP), MOD_SHIFT, MOD_WIN
quint32 native = 0;
if (modifiers & Qt::ShiftModifier)
native |= MOD_SHIFT;
if (modifiers & Qt::ControlModifier)
native |= MOD_CONTROL;
if (modifiers & Qt::AltModifier)
native |= MOD_ALT;
if (modifiers & Qt::MetaModifier)
native |= MOD_WIN;
// TODO: resolve these?
//if (modifiers & Qt::KeypadModifier)
//if (modifiers & Qt::GroupSwitchModifier)
return native;
}
quint32 QxtGlobalShortcutPrivate::nativeKeycode(Qt::Key key)
{
switch (key)
{
case Qt::Key_Escape:
return VK_ESCAPE;
case Qt::Key_Tab:
case Qt::Key_Backtab:
return VK_TAB;
case Qt::Key_Backspace:
return VK_BACK;
case Qt::Key_Return:
case Qt::Key_Enter:
return VK_RETURN;
case Qt::Key_Insert:
return VK_INSERT;
case Qt::Key_Delete:
return VK_DELETE;
case Qt::Key_Pause:
return VK_PAUSE;
case Qt::Key_Print:
return VK_PRINT;
case Qt::Key_Clear:
return VK_CLEAR;
case Qt::Key_Home:
return VK_HOME;
case Qt::Key_End:
return VK_END;
case Qt::Key_Left:
return VK_LEFT;
case Qt::Key_Up:
return VK_UP;
case Qt::Key_Right:
return VK_RIGHT;
case Qt::Key_Down:
return VK_DOWN;
case Qt::Key_PageUp:
return VK_PRIOR;
case Qt::Key_PageDown:
return VK_NEXT;
case Qt::Key_F1:
return VK_F1;
case Qt::Key_F2:
return VK_F2;
case Qt::Key_F3:
return VK_F3;
case Qt::Key_F4:
return VK_F4;
case Qt::Key_F5:
return VK_F5;
case Qt::Key_F6:
return VK_F6;
case Qt::Key_F7:
return VK_F7;
case Qt::Key_F8:
return VK_F8;
case Qt::Key_F9:
return VK_F9;
case Qt::Key_F10:
return VK_F10;
case Qt::Key_F11:
return VK_F11;
case Qt::Key_F12:
return VK_F12;
case Qt::Key_F13:
return VK_F13;
case Qt::Key_F14:
return VK_F14;
case Qt::Key_F15:
return VK_F15;
case Qt::Key_F16:
return VK_F16;
case Qt::Key_F17:
return VK_F17;
case Qt::Key_F18:
return VK_F18;
case Qt::Key_F19:
return VK_F19;
case Qt::Key_F20:
return VK_F20;
case Qt::Key_F21:
return VK_F21;
case Qt::Key_F22:
return VK_F22;
case Qt::Key_F23:
return VK_F23;
case Qt::Key_F24:
return VK_F24;
case Qt::Key_Space:
return VK_SPACE;
case Qt::Key_Asterisk:
return VK_MULTIPLY;
case Qt::Key_Plus:
return VK_ADD;
case Qt::Key_Comma:
return VK_SEPARATOR;
case Qt::Key_Minus:
return VK_SUBTRACT;
case Qt::Key_Slash:
return VK_DIVIDE;
case Qt::Key_MediaNext:
return VK_MEDIA_NEXT_TRACK;
case Qt::Key_MediaPrevious:
return VK_MEDIA_PREV_TRACK;
case Qt::Key_MediaPlay:
return VK_MEDIA_PLAY_PAUSE;
case Qt::Key_MediaStop:
return VK_MEDIA_STOP;
// couldn't find those in VK_*
//case Qt::Key_MediaLast:
//case Qt::Key_MediaRecord:
case Qt::Key_VolumeDown:
return VK_VOLUME_DOWN;
case Qt::Key_VolumeUp:
return VK_VOLUME_UP;
case Qt::Key_VolumeMute:
return VK_VOLUME_MUTE;
// numbers
case Qt::Key_0:
case Qt::Key_1:
case Qt::Key_2:
case Qt::Key_3:
case Qt::Key_4:
case Qt::Key_5:
case Qt::Key_6:
case Qt::Key_7:
case Qt::Key_8:
case Qt::Key_9:
return key;
// letters
case Qt::Key_A:
case Qt::Key_B:
case Qt::Key_C:
case Qt::Key_D:
case Qt::Key_E:
case Qt::Key_F:
case Qt::Key_G:
case Qt::Key_H:
case Qt::Key_I:
case Qt::Key_J:
case Qt::Key_K:
case Qt::Key_L:
case Qt::Key_M:
case Qt::Key_N:
case Qt::Key_O:
case Qt::Key_P:
case Qt::Key_Q:
case Qt::Key_R:
case Qt::Key_S:
case Qt::Key_T:
case Qt::Key_U:
case Qt::Key_V:
case Qt::Key_W:
case Qt::Key_X:
case Qt::Key_Y:
case Qt::Key_Z:
return key;
default:
return 0;
}
}
bool QxtGlobalShortcutPrivate::registerShortcut(quint32 nativeKey, quint32 nativeMods)
{
return RegisterHotKey(0, nativeMods ^ nativeKey, nativeMods, nativeKey);
}
bool QxtGlobalShortcutPrivate::unregisterShortcut(quint32 nativeKey, quint32 nativeMods)
{
return UnregisterHotKey(0, nativeMods ^ nativeKey);
}

View File

@@ -1,263 +0,0 @@
#include "qxtglobalshortcut_p.h"
/****************************************************************************
** Copyright (c) 2006 - 2011, the LibQxt project.
** See the Qxt AUTHORS file for a list of authors and copyright holders.
** All rights reserved.
**
** Redistribution and use in source and binary forms, with or without
** modification, are permitted provided that the following conditions are met:
** * Redistributions of source code must retain the above copyright
** notice, this list of conditions and the following disclaimer.
** * Redistributions in binary form must reproduce the above copyright
** notice, this list of conditions and the following disclaimer in the
** documentation and/or other materials provided with the distribution.
** * Neither the name of the LibQxt project 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 <COPYRIGHT HOLDER> 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.
**
** <http://libqxt.org> <foundation@libqxt.org>
*****************************************************************************/
#if QT_VERSION < QT_VERSION_CHECK(5,0,0)
# include <QX11Info>
#else
# include <qpa/qplatformnativeinterface.h>
# include <xcb/xcb.h>
# include <QApplication>
#endif
#include <X11/X.h>
#include <X11/Xlib.h>
#include <xcb/xproto.h>
#include <QtGlobal>
#include <QByteArray>
#include <QGuiApplication>
#include <QKeySequence>
#include <QString>
#include <QVector>
#include "keymapper_x11.h"
namespace {
const QVector<quint32> maskModifiers = QVector<quint32>()
<< 0 << Mod2Mask << LockMask << (Mod2Mask | LockMask);
typedef int (*X11ErrorHandler)(Display *display, XErrorEvent *event);
class QxtX11ErrorHandler {
public:
static bool error;
static int qxtX11ErrorHandler(Display *display, XErrorEvent *event)
{
Q_UNUSED(display);
switch (event->error_code)
{
case BadAccess:
case BadValue:
case BadWindow:
if (event->request_code == 33 /* X_GrabKey */ ||
event->request_code == 34 /* X_UngrabKey */)
{
error = true;
//TODO:
//char errstr[256];
//XGetErrorText(dpy, err->error_code, errstr, 256);
}
}
return 0;
}
QxtX11ErrorHandler()
{
error = false;
m_previousErrorHandler = XSetErrorHandler(qxtX11ErrorHandler);
}
~QxtX11ErrorHandler()
{
XSetErrorHandler(m_previousErrorHandler);
}
private:
X11ErrorHandler m_previousErrorHandler;
};
bool QxtX11ErrorHandler::error = false;
class QxtX11Data {
public:
QxtX11Data()
{
#if QT_VERSION < QT_VERSION_CHECK(5,0,0)
m_display = QX11Info::display();
#else
QPlatformNativeInterface *native = qApp->platformNativeInterface();
void *display = native->nativeResourceForScreen(QByteArray("display"),
QGuiApplication::primaryScreen());
m_display = reinterpret_cast<Display *>(display);
#endif
}
bool isValid()
{
return m_display != 0;
}
Display *display()
{
Q_ASSERT(isValid());
return m_display;
}
Window rootWindow()
{
return DefaultRootWindow(display());
}
bool grabKey(quint32 keycode, quint32 modifiers, Window window)
{
QxtX11ErrorHandler errorHandler;
for (int i = 0; !errorHandler.error && i < maskModifiers.size(); ++i) {
XGrabKey(display(), keycode, modifiers | maskModifiers[i], window, True,
GrabModeAsync, GrabModeAsync);
}
if (errorHandler.error) {
ungrabKey(keycode, modifiers, window);
return false;
}
return true;
}
bool ungrabKey(quint32 keycode, quint32 modifiers, Window window)
{
QxtX11ErrorHandler errorHandler;
foreach (quint32 maskMods, maskModifiers) {
XUngrabKey(display(), keycode, modifiers | maskMods, window);
}
return !errorHandler.error;
}
private:
Display *m_display;
};
} // namespace
#if QT_VERSION < QT_VERSION_CHECK(5,0,0)
bool QxtGlobalShortcutPrivate::eventFilter(void *message)
{
XEvent *event = static_cast<XEvent *>(message);
if (event->type == KeyPress)
{
XKeyEvent *key = reinterpret_cast<XKeyEvent *>(event);
unsigned int keycode = key->keycode;
unsigned int keystate = key->state;
#else
bool QxtGlobalShortcutPrivate::nativeEventFilter(const QByteArray & eventType,
void *message, long *result)
{
Q_UNUSED(result);
xcb_key_press_event_t *kev = 0;
if (eventType == "xcb_generic_event_t") {
xcb_generic_event_t *ev = static_cast<xcb_generic_event_t *>(message);
if ((ev->response_type & 127) == XCB_KEY_PRESS)
kev = static_cast<xcb_key_press_event_t *>(message);
}
if (kev != 0) {
unsigned int keycode = kev->detail;
unsigned int keystate = 0;
if(kev->state & XCB_MOD_MASK_1)
keystate |= Mod1Mask;
if(kev->state & XCB_MOD_MASK_CONTROL)
keystate |= ControlMask;
if(kev->state & XCB_MOD_MASK_4)
keystate |= Mod4Mask;
if(kev->state & XCB_MOD_MASK_SHIFT)
keystate |= ShiftMask;
#endif
activateShortcut(keycode,
// Mod1Mask == Alt, Mod4Mask == Meta
keystate & (ShiftMask | ControlMask | Mod1Mask | Mod4Mask));
}
return false;
}
quint32 QxtGlobalShortcutPrivate::nativeModifiers(Qt::KeyboardModifiers modifiers)
{
// ShiftMask, LockMask, ControlMask, Mod1Mask, Mod2Mask, Mod3Mask, Mod4Mask, and Mod5Mask
quint32 native = 0;
if (modifiers & Qt::ShiftModifier)
native |= ShiftMask;
if (modifiers & Qt::ControlModifier)
native |= ControlMask;
if (modifiers & Qt::AltModifier)
native |= Mod1Mask;
if (modifiers & Qt::MetaModifier)
native |= Mod4Mask;
// TODO: resolve these?
//if (modifiers & Qt::MetaModifier)
//if (modifiers & Qt::KeypadModifier)
//if (modifiers & Qt::GroupSwitchModifier)
return native;
}
quint32 QxtGlobalShortcutPrivate::nativeKeycode(Qt::Key key)
{
// (davidsansome) Try the table from QKeyMapper first - this seems to be
// the only way to get Keysyms for the media keys.
unsigned int keysym = 0;
int i = 0;
while (KeyTbl[i]) {
if (KeyTbl[i+1] == static_cast<uint>(key)) {
keysym = KeyTbl[i];
break;
}
i += 2;
}
// If that didn't work then fall back on XStringToKeysym
if (!keysym) {
keysym = XStringToKeysym(QKeySequence(key).toString().toLatin1().data());
if (keysym == NoSymbol)
keysym = static_cast<ushort>(key);
}
QxtX11Data x11;
if (!x11.isValid())
return 0;
return XKeysymToKeycode(x11.display(), keysym);
}
bool QxtGlobalShortcutPrivate::registerShortcut(quint32 nativeKey, quint32 nativeMods)
{
QxtX11Data x11;
return x11.isValid() && x11.grabKey(nativeKey, nativeMods, x11.rootWindow());
}
bool QxtGlobalShortcutPrivate::unregisterShortcut(quint32 nativeKey, quint32 nativeMods)
{
QxtX11Data x11;
return x11.isValid() && x11.ungrabKey(nativeKey, nativeMods, x11.rootWindow());
}

View File

@@ -1,4 +0,0 @@
cmake_minimum_required(VERSION 2.8.11)
set(CMAKE_CXX_STANDARD 11)
add_library(sha2 STATIC sha2.cpp)

589
3rdparty/sha2/sha2.cpp vendored
View File

@@ -1,589 +0,0 @@
/*
* FILE: sha2.c
* AUTHOR: Aaron D. Gifford - http://www.aarongifford.com/
*
* Copyright (c) 2000-2001, Aaron D. Gifford
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the copyright holder nor the names of contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTOR(S) ``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 AUTHOR OR CONTRIBUTOR(S) 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.
*
*/
#include <string.h>
#include <assert.h>
#include "sha2.h"
/*
* ASSERT NOTE:
* Some sanity checking code is included using assert(). On my FreeBSD
* system, this additional code can be removed by compiling with NDEBUG
* defined. Check your own systems manpage on assert() to see how to
* compile WITHOUT the sanity checking code on your system.
*
* UNROLLED TRANSFORM LOOP NOTE:
* You can define SHA2_UNROLL_TRANSFORM to use the unrolled transform
* loop version for the hash transform rounds (defined using macros
* later in this file). Either define on the command line, for example:
*
* cc -DSHA2_UNROLL_TRANSFORM -o sha2 sha2.c sha2prog.c
*
* or define below:
*
* #define SHA2_UNROLL_TRANSFORM
*
*/
/*** SHA-256/384/512 Machine Architecture Definitions *****************/
/*
* BYTE_ORDER NOTE:
*
* Please make sure that your system defines BYTE_ORDER. If your
* architecture is little-endian, make sure it also defines
* LITTLE_ENDIAN and that the two (BYTE_ORDER and LITTLE_ENDIAN) are
* equivilent.
*
* If your system does not define the above, then you can do so by
* hand like this:
*
* #define LITTLE_ENDIAN 1234
* #define BIG_ENDIAN 4321
*
* And for little-endian machines, add:
*
* #define BYTE_ORDER LITTLE_ENDIAN
*
* Or for big-endian machines:
*
* #define BYTE_ORDER BIG_ENDIAN
*
* The FreeBSD machine this was written on defines BYTE_ORDER
* appropriately by including <sys/types.h> (which in turn includes
* <machine/endian.h> where the appropriate definitions are actually
* made).
*/
#ifdef __MINGW32__
#include <sys/param.h>
#endif
#if !defined(BYTE_ORDER) || (BYTE_ORDER != LITTLE_ENDIAN && BYTE_ORDER != BIG_ENDIAN)
#error Define BYTE_ORDER to be equal to either LITTLE_ENDIAN or BIG_ENDIAN
#endif
namespace strawberry_sha2 {
/*
* Define the followingsha2_* types to types of the correct length on
* the native archtecture. Most BSD systems and Linux define u_intXX_t
* types. Machines with very recent ANSI C headers, can use the
* uintXX_t definintions from inttypes.h by defining SHA2_USE_INTTYPES_H
* during compile or in the sha.h header file.
*
* Machines that support neither u_intXX_t nor inttypes.h's uintXX_t
* will need to define these three typedefs below (and the appropriate
* ones in sha.h too) by hand according to their system architecture.
*
* Thank you, Jun-ichiro itojun Hagino, for suggesting using u_intXX_t
* types and pointing out recent ANSI C support for uintXX_t in inttypes.h.
*/
#ifdef SHA2_USE_INTTYPES_H
typedef uint8_t sha2_byte; /* Exactly 1 byte */
typedef uint32_t sha2_word32; /* Exactly 4 bytes */
typedef uint64_t sha2_word64; /* Exactly 8 bytes */
#else /* SHA2_USE_INTTYPES_H */
typedef u_int8_t sha2_byte; /* Exactly 1 byte */
typedef u_int32_t sha2_word32; /* Exactly 4 bytes */
typedef u_int64_t sha2_word64; /* Exactly 8 bytes */
#endif /* SHA2_USE_INTTYPES_H */
/*** SHA-256/384/512 Various Length Definitions ***********************/
/* NOTE: Most of these are in sha2.h */
#define SHA256_SHORT_BLOCK_LENGTH (SHA256_BLOCK_LENGTH - 8)
/*** ENDIAN REVERSAL MACROS *******************************************/
#if BYTE_ORDER == LITTLE_ENDIAN
#define REVERSE32(w,x) { \
sha2_word32 tmp = (w); \
tmp = (tmp >> 16) | (tmp << 16); \
(x) = ((tmp & 0xff00ff00UL) >> 8) | ((tmp & 0x00ff00ffUL) << 8); \
}
#define REVERSE64(w,x) { \
sha2_word64 tmp = (w); \
tmp = (tmp >> 32) | (tmp << 32); \
tmp = ((tmp & 0xff00ff00ff00ff00ULL) >> 8) | \
((tmp & 0x00ff00ff00ff00ffULL) << 8); \
(x) = ((tmp & 0xffff0000ffff0000ULL) >> 16) | \
((tmp & 0x0000ffff0000ffffULL) << 16); \
}
#endif /* BYTE_ORDER == LITTLE_ENDIAN */
/*
* Macro for incrementally adding the unsigned 64-bit integer n to the
* unsigned 128-bit integer (represented using a two-element array of
* 64-bit words):
*/
#define ADDINC128(w,n) { \
(w)[0] += (sha2_word64)(n); \
if ((w)[0] < (n)) { \
(w)[1]++; \
} \
}
/*
* Macros for copying blocks of memory and for zeroing out ranges
* of memory. Using these macros makes it easy to switch from
* using memset()/memcpy() and using bzero()/bcopy().
*
* Please define either SHA2_USE_MEMSET_MEMCPY or define
* SHA2_USE_BZERO_BCOPY depending on which function set you
* choose to use:
*/
#if !defined(SHA2_USE_MEMSET_MEMCPY) && !defined(SHA2_USE_BZERO_BCOPY)
/* Default to memset()/memcpy() if no option is specified */
#define SHA2_USE_MEMSET_MEMCPY 1
#endif
#if defined(SHA2_USE_MEMSET_MEMCPY) && defined(SHA2_USE_BZERO_BCOPY)
/* Abort with an error if BOTH options are defined */
#error Define either SHA2_USE_MEMSET_MEMCPY or SHA2_USE_BZERO_BCOPY, not both!
#endif
#ifdef SHA2_USE_MEMSET_MEMCPY
#define MEMSET_BZERO(p,l) memset((p), 0, (l))
#define MEMCPY_BCOPY(d,s,l) memcpy((d), (s), (l))
#endif
#ifdef SHA2_USE_BZERO_BCOPY
#define MEMSET_BZERO(p,l) bzero((p), (l))
#define MEMCPY_BCOPY(d,s,l) bcopy((s), (d), (l))
#endif
/*** THE SIX LOGICAL FUNCTIONS ****************************************/
/*
* Bit shifting and rotation (used by the six SHA-XYZ logical functions:
*
* NOTE: The naming of R and S appears backwards here (R is a SHIFT and
* S is a ROTATION) because the SHA-256/384/512 description document
* (see http://csrc.nist.gov/cryptval/shs/sha256-384-512.pdf) uses this
* same "backwards" definition.
*/
/* Shift-right (used in SHA-256, SHA-384, and SHA-512): */
#define R(b,x) ((x) >> (b))
/* 32-bit Rotate-right (used in SHA-256): */
#define S32(b,x) (((x) >> (b)) | ((x) << (32 - (b))))
/* Two of six logical functions used in SHA-256, SHA-384, and SHA-512: */
#define Ch(x,y,z) (((x) & (y)) ^ ((~(x)) & (z)))
#define Maj(x,y,z) (((x) & (y)) ^ ((x) & (z)) ^ ((y) & (z)))
/* Four of six logical functions used in SHA-256: */
#define Sigma0_256(x) (S32(2, (x)) ^ S32(13, (x)) ^ S32(22, (x)))
#define Sigma1_256(x) (S32(6, (x)) ^ S32(11, (x)) ^ S32(25, (x)))
#define sigma0_256(x) (S32(7, (x)) ^ S32(18, (x)) ^ R(3 , (x)))
#define sigma1_256(x) (S32(17, (x)) ^ S32(19, (x)) ^ R(10, (x)))
/*** INTERNAL FUNCTION PROTOTYPES *************************************/
/* NOTE: These should not be accessed directly from outside this
* library -- they are intended for private internal visibility/use
* only.
*/
void SHA256_Transform(SHA256_CTX*, const sha2_word32*);
/*** SHA-XYZ INITIAL HASH VALUES AND CONSTANTS ************************/
/* Hash constant words K for SHA-256: */
const static sha2_word32 K256[64] = {
0x428a2f98UL, 0x71374491UL, 0xb5c0fbcfUL, 0xe9b5dba5UL,
0x3956c25bUL, 0x59f111f1UL, 0x923f82a4UL, 0xab1c5ed5UL,
0xd807aa98UL, 0x12835b01UL, 0x243185beUL, 0x550c7dc3UL,
0x72be5d74UL, 0x80deb1feUL, 0x9bdc06a7UL, 0xc19bf174UL,
0xe49b69c1UL, 0xefbe4786UL, 0x0fc19dc6UL, 0x240ca1ccUL,
0x2de92c6fUL, 0x4a7484aaUL, 0x5cb0a9dcUL, 0x76f988daUL,
0x983e5152UL, 0xa831c66dUL, 0xb00327c8UL, 0xbf597fc7UL,
0xc6e00bf3UL, 0xd5a79147UL, 0x06ca6351UL, 0x14292967UL,
0x27b70a85UL, 0x2e1b2138UL, 0x4d2c6dfcUL, 0x53380d13UL,
0x650a7354UL, 0x766a0abbUL, 0x81c2c92eUL, 0x92722c85UL,
0xa2bfe8a1UL, 0xa81a664bUL, 0xc24b8b70UL, 0xc76c51a3UL,
0xd192e819UL, 0xd6990624UL, 0xf40e3585UL, 0x106aa070UL,
0x19a4c116UL, 0x1e376c08UL, 0x2748774cUL, 0x34b0bcb5UL,
0x391c0cb3UL, 0x4ed8aa4aUL, 0x5b9cca4fUL, 0x682e6ff3UL,
0x748f82eeUL, 0x78a5636fUL, 0x84c87814UL, 0x8cc70208UL,
0x90befffaUL, 0xa4506cebUL, 0xbef9a3f7UL, 0xc67178f2UL
};
/* Initial hash value H for SHA-256: */
const static sha2_word32 sha256_initial_hash_value[8] = {
0x6a09e667UL,
0xbb67ae85UL,
0x3c6ef372UL,
0xa54ff53aUL,
0x510e527fUL,
0x9b05688cUL,
0x1f83d9abUL,
0x5be0cd19UL
};
/*
* Constant used by SHA256/384/512_End() functions for converting the
* digest to a readable hexadecimal character string:
*/
static const char *sha2_hex_digits = "0123456789abcdef";
/*** SHA-256: *********************************************************/
void SHA256_Init(SHA256_CTX* context) {
if (context == (SHA256_CTX*)0) {
return;
}
MEMCPY_BCOPY(context->state, sha256_initial_hash_value, SHA256_DIGEST_LENGTH);
MEMSET_BZERO(context->buffer, SHA256_BLOCK_LENGTH);
context->bitcount = 0;
}
#ifdef SHA2_UNROLL_TRANSFORM
/* Unrolled SHA-256 round macros: */
#if BYTE_ORDER == LITTLE_ENDIAN
#define ROUND256_0_TO_15(a,b,c,d,e,f,g,h) \
REVERSE32(*data++, W256[j]); \
T1 = (h) + Sigma1_256(e) + Ch((e), (f), (g)) + \
K256[j] + W256[j]; \
(d) += T1; \
(h) = T1 + Sigma0_256(a) + Maj((a), (b), (c)); \
j++
#else /* BYTE_ORDER == LITTLE_ENDIAN */
#define ROUND256_0_TO_15(a,b,c,d,e,f,g,h) \
T1 = (h) + Sigma1_256(e) + Ch((e), (f), (g)) + \
K256[j] + (W256[j] = *data++); \
(d) += T1; \
(h) = T1 + Sigma0_256(a) + Maj((a), (b), (c)); \
j++
#endif /* BYTE_ORDER == LITTLE_ENDIAN */
#define ROUND256(a,b,c,d,e,f,g,h) \
s0 = W256[(j+1)&0x0f]; \
s0 = sigma0_256(s0); \
s1 = W256[(j+14)&0x0f]; \
s1 = sigma1_256(s1); \
T1 = (h) + Sigma1_256(e) + Ch((e), (f), (g)) + K256[j] + \
(W256[j&0x0f] += s1 + W256[(j+9)&0x0f] + s0); \
(d) += T1; \
(h) = T1 + Sigma0_256(a) + Maj((a), (b), (c)); \
j++
void SHA256_Transform(SHA256_CTX* context, const sha2_word32* data) {
sha2_word32 a, b, c, d, e, f, g, h, s0, s1;
sha2_word32 T1, *W256;
int j;
W256 = (sha2_word32*)context->buffer;
/* Initialize registers with the prev. intermediate value */
a = context->state[0];
b = context->state[1];
c = context->state[2];
d = context->state[3];
e = context->state[4];
f = context->state[5];
g = context->state[6];
h = context->state[7];
j = 0;
do {
/* Rounds 0 to 15 (unrolled): */
ROUND256_0_TO_15(a,b,c,d,e,f,g,h);
ROUND256_0_TO_15(h,a,b,c,d,e,f,g);
ROUND256_0_TO_15(g,h,a,b,c,d,e,f);
ROUND256_0_TO_15(f,g,h,a,b,c,d,e);
ROUND256_0_TO_15(e,f,g,h,a,b,c,d);
ROUND256_0_TO_15(d,e,f,g,h,a,b,c);
ROUND256_0_TO_15(c,d,e,f,g,h,a,b);
ROUND256_0_TO_15(b,c,d,e,f,g,h,a);
} while (j < 16);
/* Now for the remaining rounds to 64: */
do {
ROUND256(a,b,c,d,e,f,g,h);
ROUND256(h,a,b,c,d,e,f,g);
ROUND256(g,h,a,b,c,d,e,f);
ROUND256(f,g,h,a,b,c,d,e);
ROUND256(e,f,g,h,a,b,c,d);
ROUND256(d,e,f,g,h,a,b,c);
ROUND256(c,d,e,f,g,h,a,b);
ROUND256(b,c,d,e,f,g,h,a);
} while (j < 64);
/* Compute the current intermediate hash value */
context->state[0] += a;
context->state[1] += b;
context->state[2] += c;
context->state[3] += d;
context->state[4] += e;
context->state[5] += f;
context->state[6] += g;
context->state[7] += h;
/* Clean up */
a = b = c = d = e = f = g = h = T1 = 0;
}
#else /* SHA2_UNROLL_TRANSFORM */
void SHA256_Transform(SHA256_CTX* context, const sha2_word32* data) {
sha2_word32 a, b, c, d, e, f, g, h, s0, s1;
sha2_word32 T1, T2, *W256;
int j;
W256 = (sha2_word32*)context->buffer;
/* Initialize registers with the prev. intermediate value */
a = context->state[0];
b = context->state[1];
c = context->state[2];
d = context->state[3];
e = context->state[4];
f = context->state[5];
g = context->state[6];
h = context->state[7];
j = 0;
do {
#if BYTE_ORDER == LITTLE_ENDIAN
/* Copy data while converting to host byte order */
REVERSE32(*data++,W256[j]);
/* Apply the SHA-256 compression function to update a..h */
T1 = h + Sigma1_256(e) + Ch(e, f, g) + K256[j] + W256[j];
#else /* BYTE_ORDER == LITTLE_ENDIAN */
/* Apply the SHA-256 compression function to update a..h with copy */
T1 = h + Sigma1_256(e) + Ch(e, f, g) + K256[j] + (W256[j] = *data++);
#endif /* BYTE_ORDER == LITTLE_ENDIAN */
T2 = Sigma0_256(a) + Maj(a, b, c);
h = g;
g = f;
f = e;
e = d + T1;
d = c;
c = b;
b = a;
a = T1 + T2;
j++;
} while (j < 16);
do {
/* Part of the message block expansion: */
s0 = W256[(j+1)&0x0f];
s0 = sigma0_256(s0);
s1 = W256[(j+14)&0x0f];
s1 = sigma1_256(s1);
/* Apply the SHA-256 compression function to update a..h */
T1 = h + Sigma1_256(e) + Ch(e, f, g) + K256[j] +
(W256[j&0x0f] += s1 + W256[(j+9)&0x0f] + s0);
T2 = Sigma0_256(a) + Maj(a, b, c);
h = g;
g = f;
f = e;
e = d + T1;
d = c;
c = b;
b = a;
a = T1 + T2;
j++;
} while (j < 64);
/* Compute the current intermediate hash value */
context->state[0] += a;
context->state[1] += b;
context->state[2] += c;
context->state[3] += d;
context->state[4] += e;
context->state[5] += f;
context->state[6] += g;
context->state[7] += h;
/* Clean up */
a = b = c = d = e = f = g = h = T1 = T2 = 0;
}
#endif /* SHA2_UNROLL_TRANSFORM */
void SHA256_Update(SHA256_CTX* context, const sha2_byte *data, size_t len) {
unsigned int freespace, usedspace;
if (len == 0) {
/* Calling with no data is valid - we do nothing */
return;
}
/* Sanity check: */
assert(context != (SHA256_CTX*)0 && data != (sha2_byte*)0);
usedspace = (context->bitcount >> 3) % SHA256_BLOCK_LENGTH;
if (usedspace > 0) {
/* Calculate how much free space is available in the buffer */
freespace = SHA256_BLOCK_LENGTH - usedspace;
if (len >= freespace) {
/* Fill the buffer completely and process it */
MEMCPY_BCOPY(&context->buffer[usedspace], data, freespace);
context->bitcount += freespace << 3;
len -= freespace;
data += freespace;
SHA256_Transform(context, (sha2_word32*)context->buffer);
} else {
/* The buffer is not yet full */
MEMCPY_BCOPY(&context->buffer[usedspace], data, len);
context->bitcount += len << 3;
/* Clean up: */
usedspace = freespace = 0;
return;
}
}
while (len >= SHA256_BLOCK_LENGTH) {
/* Process as many complete blocks as we can */
SHA256_Transform(context, (sha2_word32*)data);
context->bitcount += SHA256_BLOCK_LENGTH << 3;
len -= SHA256_BLOCK_LENGTH;
data += SHA256_BLOCK_LENGTH;
}
if (len > 0) {
/* There's left-overs, so save 'em */
MEMCPY_BCOPY(context->buffer, data, len);
context->bitcount += len << 3;
}
/* Clean up: */
usedspace = freespace = 0;
}
void SHA256_Final(sha2_byte digest[], SHA256_CTX* context) {
sha2_word32 *d = (sha2_word32*)digest;
unsigned int usedspace;
/* Sanity check: */
assert(context != (SHA256_CTX*)0);
/* If no digest buffer is passed, we don't bother doing this: */
if (digest != (sha2_byte*)0) {
usedspace = (context->bitcount >> 3) % SHA256_BLOCK_LENGTH;
#if BYTE_ORDER == LITTLE_ENDIAN
/* Convert FROM host byte order */
REVERSE64(context->bitcount,context->bitcount);
#endif
if (usedspace > 0) {
/* Begin padding with a 1 bit: */
context->buffer[usedspace++] = 0x80;
if (usedspace <= SHA256_SHORT_BLOCK_LENGTH) {
/* Set-up for the last transform: */
MEMSET_BZERO(&context->buffer[usedspace], SHA256_SHORT_BLOCK_LENGTH - usedspace);
} else {
if (usedspace < SHA256_BLOCK_LENGTH) {
MEMSET_BZERO(&context->buffer[usedspace], SHA256_BLOCK_LENGTH - usedspace);
}
/* Do second-to-last transform: */
SHA256_Transform(context, (sha2_word32*)context->buffer);
/* And set-up for the last transform: */
MEMSET_BZERO(context->buffer, SHA256_SHORT_BLOCK_LENGTH);
}
} else {
/* Set-up for the last transform: */
MEMSET_BZERO(context->buffer, SHA256_SHORT_BLOCK_LENGTH);
/* Begin padding with a 1 bit: */
*context->buffer = 0x80;
}
/* Set the bit count: */
*(sha2_word64*)&context->buffer[SHA256_SHORT_BLOCK_LENGTH] = context->bitcount;
/* Final transform: */
SHA256_Transform(context, (sha2_word32*)context->buffer);
#if BYTE_ORDER == LITTLE_ENDIAN
{
/* Convert TO host byte order */
int j;
for (j = 0; j < 8; j++) {
REVERSE32(context->state[j],context->state[j]);
*d++ = context->state[j];
}
}
#else
MEMCPY_BCOPY(d, context->state, SHA256_DIGEST_LENGTH);
#endif
}
/* Clean up state data: */
MEMSET_BZERO(context, sizeof(SHA256_CTX));
usedspace = 0;
}
char *SHA256_End(SHA256_CTX* context, char buffer[]) {
sha2_byte digest[SHA256_DIGEST_LENGTH], *d = digest;
int i;
/* Sanity check: */
assert(context != (SHA256_CTX*)0);
if (buffer != (char*)0) {
SHA256_Final(digest, context);
for (i = 0; i < SHA256_DIGEST_LENGTH; i++) {
*buffer++ = sha2_hex_digits[(*d & 0xf0) >> 4];
*buffer++ = sha2_hex_digits[*d & 0x0f];
d++;
}
*buffer = (char)0;
} else {
MEMSET_BZERO(context, sizeof(SHA256_CTX));
}
MEMSET_BZERO(digest, SHA256_DIGEST_LENGTH);
return buffer;
}
char* SHA256_Data(const sha2_byte* data, size_t len, char digest[SHA256_DIGEST_STRING_LENGTH]) {
SHA256_CTX context;
SHA256_Init(&context);
SHA256_Update(&context, data, len);
return SHA256_End(&context, digest);
}
} // namespace strawberry_sha2

81
3rdparty/sha2/sha2.h vendored
View File

@@ -1,81 +0,0 @@
/*
* FILE: sha2.h
* AUTHOR: Aaron D. Gifford - http://www.aarongifford.com/
*
* Copyright (c) 2000-2001, Aaron D. Gifford
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the copyright holder nor the names of contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTOR(S) ``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 AUTHOR OR CONTRIBUTOR(S) 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.
*
* $Id: sha2.h,v 1.1 2001/11/08 00:02:01 adg Exp adg $
*/
#ifndef __STRAWBERRY_SHA2_H__
#define __STRAWBERRY_SHA2_H__
#include <stddef.h>
/*
* Import u_intXX_t size_t type definitions from system headers. You
* may need to change this, or define these things yourself in this
* file.
*/
#include <sys/types.h>
namespace strawberry_sha2 {
/*** SHA-256/384/512 Various Length Definitions ***********************/
static const int SHA256_BLOCK_LENGTH = 64;
static const int SHA256_DIGEST_LENGTH = 32;
static const int SHA256_DIGEST_STRING_LENGTH = (SHA256_DIGEST_LENGTH * 2 + 1);
/*** SHA-256/384/512 Context Structures *******************************/
/* NOTE: If your architecture does not define either u_intXX_t types or
* uintXX_t (from inttypes.h), you may need to define things by hand
* for your system:
*/
#ifdef __MINGW32__
typedef unsigned char u_int8_t; /* 1-byte (8-bits) */
typedef unsigned int u_int32_t; /* 4-bytes (32-bits) */
typedef unsigned long long u_int64_t; /* 8-bytes (64-bits) */
#endif
typedef struct _SHA256_CTX {
u_int32_t state[8];
u_int64_t bitcount;
u_int8_t buffer[SHA256_BLOCK_LENGTH];
} SHA256_CTX;
void SHA256_Init(SHA256_CTX *);
void SHA256_Update(SHA256_CTX*, const u_int8_t*, size_t);
void SHA256_Final(u_int8_t[SHA256_DIGEST_LENGTH], SHA256_CTX*);
char* SHA256_End(SHA256_CTX*, char[SHA256_DIGEST_STRING_LENGTH]);
char* SHA256_Data(const u_int8_t*, size_t, char[SHA256_DIGEST_STRING_LENGTH]);
} // namespace strawberry_sha2
#endif /* __STRAWBERRY_SHA2_H__ */

View File

@@ -0,0 +1,14 @@
cmake_minimum_required(VERSION 2.8.11)
set(CMAKE_CXX_STANDARD 11)
set(SINGLEAPP-SOURCES singleapplication.cpp singleapplication_p.cpp)
set(SINGLEAPP-MOC-HEADERS singleapplication.h singleapplication_p.h)
QT5_WRAP_CPP(SINGLEAPP-SOURCES-MOC ${SINGLEAPP-MOC-HEADERS})
ADD_LIBRARY(singleapplication STATIC ${SINGLEAPP-SOURCES} ${SINGLEAPP-SOURCES-MOC})
target_link_libraries(singleapplication Qt5::Core Qt5::Widgets Qt5::Network)
set(SINGLECOREAPP-SOURCES singlecoreapplication.cpp singlecoreapplication_p.cpp)
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)

24
3rdparty/singleapplication/LICENSE vendored Normal file
View File

@@ -0,0 +1,24 @@
The MIT License (MIT)
Copyright (c) Itay Grudev 2015 - 2016
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.
Note: Some of the examples include code not distributed under the terms of the
MIT License.

265
3rdparty/singleapplication/README.md vendored Normal file
View File

@@ -0,0 +1,265 @@
SingleApplication
=================
This is a replacement of the QtSingleApplication for `Qt5`.
Keeps the Primary Instance of your Application and kills each subsequent
instances. It can (if enabled) spawn secondary (non-related to the primary)
instances and can send data to the primary instance from secondary instances.
Usage
-----
The `SingleApplication` class inherits from whatever `Q[Core|Gui]Application`
class you specify via the `QAPPLICATION_CLASS` macro (`QCoreApplication` is the
default). Further usage is similar to the use of the `Q[Core|Gui]Application`
classes.
The library sets up a `QLocalServer` and a `QSharedMemory` block. The first
instance of your Application is your Primary Instance. It would check if the
shared memory block exists and if not it will start a `QLocalServer` and listen
for connections. Each subsequent instance of your application would check if the
shared memory block exists and if it does, it will connect to the QLocalServer
to notify the primary instance that a new instance had been started, after which
it would terminate with status code `0`. In the Primary Instance
`SingleApplication` would emit the `instanceStarted()` signal upon detecting
that a new instance had been started.
The library uses `stdlib` to terminate the program with the `exit()` function.
You can use the library as if you use any other `QCoreApplication` derived
class:
```cpp
#include <QApplication>
#include <SingleApplication.h>
int main( int argc, char* argv[] )
{
SingleApplication app( argc, argv );
return app.exec();
}
```
To include the library files I would recommend that you add it as a git
submodule to your project and include it's contents with a `.pri` file. Here is
how:
```bash
git submodule add git@github.com:itay-grudev/SingleApplication.git singleapplication
```
Then include the `singleapplication.pri` file in your `.pro` project file. Also
don't forget to specify which `QCoreApplication` class your app is using if it
is not `QCoreApplication`.
```qmake
include(singleapplication/singleapplication.pri)
DEFINES += QAPPLICATION_CLASS=QApplication
```
The `Instance Started` signal
------------------------
The SingleApplication class implements a `instanceStarted()` signal. You can
bind to that signal to raise your application's window when a new instance had
been started, for example.
```cpp
// window is a QWindow instance
QObject::connect(
&app,
&SingleApplication::instanceStarted,
&window,
&QWindow::raise
);
```
Using `SingleApplication::instance()` is a neat way to get the
`SingleApplication` instance for binding to it's signals anywhere in your
program.
__Note:__ On Windows the ability to bring the application windows to the
foreground is restricted. See [Windows specific implementations](Windows.md)
for a workaround and an example implementation.
Secondary Instances
-------------------
If you want to be able to launch additional Secondary Instances (not related to
your Primary Instance) you have to enable that with the third parameter of the
`SingleApplication` constructor. The default is `false` meaning no Secondary
Instances. Here is an example of how you would start a Secondary Instance send
a message with the command line arguments to the primary instance and then shut
down.
```cpp
int main(int argc, char *argv[])
{
SingleApplication app( argc, argv, true );
if( app.isSecondary() ) {
app.sendMessage( app.arguments().join(' ')).toUtf8() );
app.exit( 0 );
}
return app.exec();
}
```
*__Note:__ A secondary instance won't cause the emission of the
`instanceStarted()` signal by default. See `SingleApplication::Mode` for more
details.*
You can check whether your instance is a primary or secondary with the following
methods:
```cpp
app.isPrimary();
// or
app.isSecondary();
```
*__Note:__ If your Primary Instance is terminated a newly launched instance
will replace the Primary one even if the Secondary flag has been set.*
API
---
### Members
```cpp
SingleApplication::SingleApplication( int &argc, char *argv[], bool allowSecondary = false, Options options = Mode::User, int timeout = 100 )
```
Depending on whether `allowSecondary` is set, this constructor may terminate
your app if there is already a primary instance running. Additional `Options`
can be specified to set whether the SingleApplication block should work
user-wide or system-wide. Additionally the `Mode::SecondaryNotification` may be
used to notify the primary instance whenever a secondary instance had been
started (disabled by default). `timeout` specifies the maximum time in
milliseconds to wait for blocking operations.
*__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
and the secondary instance.*
*__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.*
---
```cpp
bool SingleApplication::sendMessage( QByteArray message, int timeout = 100 )
```
Sends `message` to the Primary Instance. Uses `timeout` as a the maximum timeout
in milliseconds for blocking functions
---
```cpp
bool SingleApplication::isPrimary()
```
Returns if the instance is the primary instance.
---
```cpp
bool SingleApplication::isSecondary()
```
Returns if the instance is a secondary instance.
---
```cpp
quint32 SingleApplication::instanceId()
```
Returns a unique identifier for the current instance.
---
```cpp
qint64 SingleApplication::primaryPid()
```
Returns the process ID (PID) of the primary instance.
### Signals
```cpp
void SingleApplication::instanceStarted()
```
Triggered whenever a new instance had been started, except for secondary
instances if the `Mode::SecondaryNotification` flag is not specified.
---
```cpp
void SingleApplication::receivedMessage( quint32 instanceId, QByteArray message )
```
Triggered whenever there is a message received from a secondary instance.
---
### Flags
```cpp
enum SingleApplication::Mode
```
* `Mode::User` - The SingleApplication block should apply user wide. This adds
user specific data to the key used for the shared memory and server name.
This is the default functionality.
* `Mode::System` The SingleApplication block applies system-wide.
* `Mode::SecondaryNotification` Whether to trigger `instanceStarted()` even
whenever secondary instances are started.
* `Mode::ExcludeAppPath` Excludes the application path from the server name
(and memory block) hash.
* `Mode::ExcludeAppVersion` Excludes the application version from the server
name (and memory block) hash.
*__Note:__ `Mode::SecondaryNotification` only works if set on both the primary
and the secondary instance.*
*__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.*
---
Versioning
----------
Each major version introduces either very significant changes or is not
backwards compatible with the previous version. Minor versions only add
additional features, bug fixes or performance improvements and are backwards
compatible with the previous release. See [`CHANGELOG.md`](CHANGELOG.md) for
more details.
Implementation
--------------
The library is implemented with a QSharedMemory block which is thread safe and
guarantees a race condition will not occur. It also uses a QLocalSocket to
notify the main process that a new instance had been spawned and thus invoke the
`instanceStarted()` signal and for messaging the primary instance.
Additionally the library can recover from being forcefully killed on *nix
systems and will reset the memory block given that there are no other
instances running.
License
-------
This library and it's supporting documentation are released under
`The MIT License (MIT)` with the exception of the Qt calculator examples which
is distributed under the BSD license.

View File

@@ -0,0 +1,175 @@
// The MIT License (MIT)
//
// Copyright (c) Itay Grudev 2015 - 2018
//
// 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 <QApplication>
#include <QtCore/QTime>
#include <QtCore/QThread>
#include <QtCore/QDateTime>
#include <QtCore/QByteArray>
#include <QtCore/QSharedMemory>
#include "singleapplication.h"
#include "singleapplication_p.h"
/**
* @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);
// 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;
#endif
// 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 );
}
}
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();
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;
}
d->memory->unlock();
d->connectToPrimary( timeout, SingleApplicationPrivate::NewInstance );
delete d;
::exit( EXIT_SUCCESS );
}
/**
* @brief Destructor
*/
SingleApplication::~SingleApplication()
{
Q_D(SingleApplication);
delete d;
}
bool SingleApplication::isPrimary()
{
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;
}
qint64 SingleApplication::primaryPid()
{
Q_D(SingleApplication);
return d->primaryPid();
}
bool SingleApplication::sendMessage( QByteArray message, int timeout )
{
Q_D(SingleApplication);
// 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->flush();
d->socket->waitForBytesWritten( timeout );
return dataWritten;
}

View File

@@ -0,0 +1,130 @@
// The MIT License (MIT)
//
// Copyright (c) Itay Grudev 2015 - 2018
//
// 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.
#ifndef SINGLEAPPLICATION_H
#define SINGLEAPPLICATION_H
#include <QtCore/QtGlobal>
#include <QApplication>
#include <QtNetwork/QLocalSocket>
class SingleApplicationPrivate;
/**
* @brief The SingleApplication class handles multipe instances of the same
* Application
* @see QCoreApplication
*/
class SingleApplication : public QApplication
{
Q_OBJECT
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 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 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 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 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 );
Q_SIGNALS:
void instanceStarted();
void receivedMessage( quint32 instanceId, QByteArray message );
private:
SingleApplicationPrivate *d_ptr;
Q_DECLARE_PRIVATE(SingleApplication)
};
Q_DECLARE_OPERATORS_FOR_FLAGS(SingleApplication::Options)
#endif // SINGLEAPPLICATION_H

View File

@@ -0,0 +1,404 @@
// The MIT License (MIT)
//
// Copyright (c) Itay Grudev 2015 - 2018
//
// 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.
//
// 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.
//
#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>
#include "singleapplication.h"
#include "singleapplication_p.h"
#ifdef Q_OS_WIN
#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()
{
if( socket != nullptr ) {
socket->close();
delete socket;
}
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();
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() );
inst->primary = false;
inst->secondary = 0;
inst->primaryPid = -1;
inst->checksum = blockChecksum();
}
void SingleApplicationPrivate::startPrimary()
{
Q_Q(SingleApplication);
// Successful creation means that no main process exists
// So we start a QLocalServer to listen for connections
QLocalServer::removeServer( blockServerName );
server = new QLocalServer();
// 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 );
}
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);
#endif
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);
#if (QT_VERSION >= QT_VERSION_CHECK(5, 6, 0))
headerStream.setVersion(QDataStream::Qt_5_6);
#endif
headerStream << static_cast <quint64>( initMsg.length() );
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 )
);
}
qint64 SingleApplicationPrivate::primaryPid()
{
qint64 pid;
memory->lock();
InstancesInfo* inst = static_cast<InstancesInfo*>( memory->data() );
pid = inst->primaryPid;
memory->unlock();
return pid;
}
/**
* @brief Executed when a connection has been made to the LocalServer
*/
void SingleApplicationPrivate::slotConnectionEstablished()
{
QLocalSocket *nextConnSocket = server->nextPendingConnection();
connectionMap.insert(nextConnSocket, ConnectionInfo());
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;
};
}
);
}
void SingleApplicationPrivate::readInitMessageHeader( QLocalSocket *sock )
{
if (!connectionMap.contains( sock )) {
return;
}
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 );
#endif
// 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 );
}
}
void SingleApplicationPrivate::readInitMessageBody( QLocalSocket *sock )
{
Q_Q(SingleApplication);
if (!connectionMap.contains( sock )) {
return;
}
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 );
#endif
// server name
QByteArray latin1Name;
readStream >> latin1Name;
// connection type
ConnectionType connectionType = InvalidConnection;
quint8 connTypeVal = InvalidConnection;
readStream >> connTypeVal;
connectionType = static_cast <ConnectionType>( connTypeVal );
// instance id
quint32 instanceId = 0;
readStream >> instanceId;
// checksum
quint16 msgChecksum = 0;
readStream >> msgChecksum;
const quint16 actualChecksum = qChecksum( msgBytes.constData(), static_cast<quint32>( msgBytes.length() - sizeof( quint16 ) ) );
bool isValid = readStream.status() == QDataStream::Ok &&
QLatin1String(latin1Name) == blockServerName &&
msgChecksum == actualChecksum;
if( !isValid ) {
sock->close();
return;
}
info.instanceId = instanceId;
info.stage = StageConnected;
if( connectionType == NewInstance ||
( connectionType == SecondaryInstance &&
options & SingleApplication::Mode::SecondaryNotification ) )
{
Q_EMIT q->instanceStarted();
}
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::slotClientConnectionClosed( QLocalSocket *closedSocket, quint32 instanceId )
{
if( closedSocket->bytesAvailable() > 0 )
Q_EMIT slotDataAvailable( closedSocket, instanceId );
}

View File

@@ -0,0 +1,99 @@
// The MIT License (MIT)
//
// Copyright (c) Itay Grudev 2015 - 2016
//
// 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.
//
// 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.
//
#ifndef SINGLEAPPLICATION_P_H
#define SINGLEAPPLICATION_P_H
#include <QtCore/QSharedMemory>
#include <QtNetwork/QLocalServer>
#include <QtNetwork/QLocalSocket>
#include "singleapplication.h"
struct InstancesInfo {
bool primary;
quint32 secondary;
qint64 primaryPid;
quint16 checksum;
};
struct ConnectionInfo {
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)
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);
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 );
};
#endif // SINGLEAPPLICATION_P_H

View File

@@ -0,0 +1,175 @@
// The MIT License (MIT)
//
// Copyright (c) Itay Grudev 2015 - 2018
//
// 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 <QCoreApplication>
#include <QtCore/QTime>
#include <QtCore/QThread>
#include <QtCore/QDateTime>
#include <QtCore/QByteArray>
#include <QtCore/QSharedMemory>
#include "singlecoreapplication.h"
#include "singlecoreapplication_p.h"
/**
* @brief Constructor. Checks and fires up LocalServer or closes the program
* if another instance already exists
* @param argc
* @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);
// 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;
#endif
// 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 );
}
}
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();
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;
}
d->memory->unlock();
d->connectToPrimary( timeout, SingleCoreApplicationPrivate::NewInstance );
delete d;
::exit( EXIT_SUCCESS );
}
/**
* @brief Destructor
*/
SingleCoreApplication::~SingleCoreApplication()
{
Q_D(SingleCoreApplication);
delete d;
}
bool SingleCoreApplication::isPrimary()
{
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;
}
qint64 SingleCoreApplication::primaryPid()
{
Q_D(SingleCoreApplication);
return d->primaryPid();
}
bool SingleCoreApplication::sendMessage( QByteArray message, int timeout )
{
Q_D(SingleCoreApplication);
// 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->flush();
d->socket->waitForBytesWritten( timeout );
return dataWritten;
}

View File

@@ -0,0 +1,130 @@
// The MIT License (MIT)
//
// Copyright (c) Itay Grudev 2015 - 2018
//
// 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.
#ifndef SINGLECOREAPPLICATION_H
#define SINGLECOREAPPLICATION_H
#include <QtCore/QtGlobal>
#include <QCoreApplication>
#include <QtNetwork/QLocalSocket>
class SingleCoreApplicationPrivate;
/**
* @brief The SingleCoreApplication class handles multipe instances of the same
* Application
* @see QCoreApplication
*/
class SingleCoreApplication : public QCoreApplication
{
Q_OBJECT
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 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 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 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 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 );
Q_SIGNALS:
void instanceStarted();
void receivedMessage( quint32 instanceId, QByteArray message );
private:
SingleCoreApplicationPrivate *d_ptr;
Q_DECLARE_PRIVATE(SingleCoreApplication)
};
Q_DECLARE_OPERATORS_FOR_FLAGS(SingleCoreApplication::Options)
#endif // SINGLECOREAPPLICATION_H

View File

@@ -0,0 +1,404 @@
// The MIT License (MIT)
//
// Copyright (c) Itay Grudev 2015 - 2018
//
// 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.
//
// 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.
//
#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>
#include "singlecoreapplication.h"
#include "singlecoreapplication_p.h"
#ifdef Q_OS_WIN
#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()
{
if( socket != nullptr ) {
socket->close();
delete socket;
}
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();
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() );
inst->primary = false;
inst->secondary = 0;
inst->primaryPid = -1;
inst->checksum = blockChecksum();
}
void SingleCoreApplicationPrivate::startPrimary()
{
Q_Q(SingleCoreApplication);
// Successful creation means that no main process exists
// So we start a QLocalServer to listen for connections
QLocalServer::removeServer( blockServerName );
server = new QLocalServer();
// 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 );
}
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);
#endif
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);
#if (QT_VERSION >= QT_VERSION_CHECK(5, 6, 0))
headerStream.setVersion(QDataStream::Qt_5_6);
#endif
headerStream << static_cast <quint64>( initMsg.length() );
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 )
);
}
qint64 SingleCoreApplicationPrivate::primaryPid()
{
qint64 pid;
memory->lock();
InstancesInfo* inst = static_cast<InstancesInfo*>( memory->data() );
pid = inst->primaryPid;
memory->unlock();
return pid;
}
/**
* @brief Executed when a connection has been made to the LocalServer
*/
void SingleCoreApplicationPrivate::slotConnectionEstablished()
{
QLocalSocket *nextConnSocket = server->nextPendingConnection();
connectionMap.insert(nextConnSocket, ConnectionInfo());
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;
};
}
);
}
void SingleCoreApplicationPrivate::readInitMessageHeader( QLocalSocket *sock )
{
if (!connectionMap.contains( sock )) {
return;
}
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 );
#endif
// 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 );
}
}
void SingleCoreApplicationPrivate::readInitMessageBody( QLocalSocket *sock )
{
Q_Q(SingleCoreApplication);
if (!connectionMap.contains( sock )) {
return;
}
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 );
#endif
// server name
QByteArray latin1Name;
readStream >> latin1Name;
// connection type
ConnectionType connectionType = InvalidConnection;
quint8 connTypeVal = InvalidConnection;
readStream >> connTypeVal;
connectionType = static_cast <ConnectionType>( connTypeVal );
// instance id
quint32 instanceId = 0;
readStream >> instanceId;
// checksum
quint16 msgChecksum = 0;
readStream >> msgChecksum;
const quint16 actualChecksum = qChecksum( msgBytes.constData(), static_cast<quint32>( msgBytes.length() - sizeof( quint16 ) ) );
bool isValid = readStream.status() == QDataStream::Ok &&
QLatin1String(latin1Name) == blockServerName &&
msgChecksum == actualChecksum;
if( !isValid ) {
sock->close();
return;
}
info.instanceId = instanceId;
info.stage = StageConnected;
if( connectionType == NewInstance ||
( connectionType == SecondaryInstance &&
options & SingleCoreApplication::Mode::SecondaryNotification ) )
{
Q_EMIT q->instanceStarted();
}
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::slotClientConnectionClosed( QLocalSocket *closedSocket, quint32 instanceId )
{
if( closedSocket->bytesAvailable() > 0 )
Q_EMIT slotDataAvailable( closedSocket, instanceId );
}

View File

@@ -0,0 +1,99 @@
// The MIT License (MIT)
//
// Copyright (c) Itay Grudev 2015 - 2016
//
// 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.
//
// 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.
//
#ifndef SINGLECOREAPPLICATION_P_H
#define SINGLECOREAPPLICATION_P_H
#include <QtCore/QSharedMemory>
#include <QtNetwork/QLocalServer>
#include <QtNetwork/QLocalSocket>
#include "singlecoreapplication.h"
struct InstancesInfo {
bool primary;
quint32 secondary;
qint64 primaryPid;
quint16 checksum;
};
struct ConnectionInfo {
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)
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);
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 );
};
#endif // SINGLECOREAPPLICATION_P_H

View File

@@ -47,6 +47,8 @@ include_directories(
${CMAKE_CURRENT_SOURCE_DIR}/s3m
${CMAKE_CURRENT_SOURCE_DIR}/it
${CMAKE_CURRENT_SOURCE_DIR}/xm
${CMAKE_CURRENT_SOURCE_DIR}/dsf
${CMAKE_CURRENT_SOURCE_DIR}/dsdiff
${CMAKE_SOURCE_DIR}/3rdparty
)
@@ -161,6 +163,11 @@ set(tag_HDRS
s3m/s3mproperties.h
xm/xmfile.h
xm/xmproperties.h
dsf/dsffile.h
dsf/dsfproperties.h
dsdiff/dsdifffile.h
dsdiff/dsdiffproperties.h
dsdiff/dsdiffdiintag.h
)
set(mpeg_SRCS
@@ -316,6 +323,17 @@ set(xm_SRCS
xm/xmproperties.cpp
)
set(dsf_SRCS
dsf/dsffile.cpp
dsf/dsfproperties.cpp
)
set(dsdiff_SRCS
dsdiff/dsdifffile.cpp
dsdiff/dsdiffproperties.cpp
dsdiff/dsdiffdiintag.cpp
)
set(toolkit_SRCS
toolkit/tstring.cpp
toolkit/tstringlist.cpp
@@ -347,7 +365,7 @@ set(tag_LIB_SRCS
${mpeg_SRCS} ${id3v1_SRCS} ${id3v2_SRCS} ${frames_SRCS} ${ogg_SRCS}
${vorbis_SRCS} ${oggflacs_SRCS} ${mpc_SRCS} ${ape_SRCS} ${toolkit_SRCS} ${flacs_SRCS}
${wavpack_SRCS} ${speex_SRCS} ${trueaudio_SRCS} ${riff_SRCS} ${aiff_SRCS} ${wav_SRCS}
${asf_SRCS} ${mp4_SRCS} ${mod_SRCS} ${s3m_SRCS} ${it_SRCS} ${xm_SRCS} ${opus_SRCS}
${asf_SRCS} ${mp4_SRCS} ${mod_SRCS} ${s3m_SRCS} ${it_SRCS} ${xm_SRCS} ${opus_SRCS} ${dsf_SRCS} ${dsdiff_SRCS}
${zlib_SRCS}
tag.cpp
tagunion.cpp

26
3rdparty/taglib/README.md vendored Normal file
View File

@@ -0,0 +1,26 @@
# TagLib
[![Build Status](https://travis-ci.org/taglib/taglib.svg?branch=master)](https://travis-ci.org/taglib/taglib)
### TagLib Audio Metadata Library
http://taglib.org/
TagLib is a library for reading and editing the metadata of several
popular audio formats. Currently it supports both ID3v1 and [ID3v2][]
for MP3 files, [Ogg Vorbis][] comments and ID3 tags
in [FLAC][], MPC, Speex, WavPack, TrueAudio, WAV, AIFF, MP4, APE,
DSF, DFF, and ASF files.
TagLib is distributed under the [GNU Lesser General Public License][]
(LGPL) and [Mozilla Public License][] (MPL). Essentially that means that
it may be used in proprietary applications, but if changes are made to
TagLib they must be contributed back to the project. Please review the
licenses if you are considering using TagLib in your project.
[ID3v2]: http://www.id3.org
[Ogg Vorbis]: http://vorbis.com/
[FLAC]: https://xiph.org/flac/
[GNU Lesser General Public License]: http://www.gnu.org/licenses/lgpl.html
[Mozilla Public License]: http://www.mozilla.org/MPL/MPL-1.1.html

View File

@@ -38,6 +38,8 @@
#include "vorbisproperties.h"
#include "wavproperties.h"
#include "wavpackproperties.h"
#include "dsfproperties.h"
#include "dsdiffproperties.h"
#include "audioproperties.h"
@@ -73,6 +75,10 @@ using namespace TagLib;
return dynamic_cast<const Vorbis::Properties*>(this)->function_name(); \
else if(dynamic_cast<const WavPack::Properties*>(this)) \
return dynamic_cast<const WavPack::Properties*>(this)->function_name(); \
else if(dynamic_cast<const DSF::Properties*>(this)) \
return dynamic_cast<const DSF::Properties*>(this)->function_name(); \
else if(dynamic_cast<const DSDIFF::Properties*>(this)) \
return dynamic_cast<const DSDIFF::Properties*>(this)->function_name(); \
else \
return (default_value);

162
3rdparty/taglib/dsdiff/dsdiffdiintag.cpp vendored Normal file
View File

@@ -0,0 +1,162 @@
/***************************************************************************
copyright : (C) 2016 by Damien Plisson, Audirvana
email : damien78@audirvana.com
***************************************************************************/
/***************************************************************************
* This library is free software; you can redistribute it and/or modify *
* it under the terms of the GNU Lesser General Public License version *
* 2.1 as published by the Free Software Foundation. *
* *
* This library is distributed in the hope that it will be useful, but *
* WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
* Lesser General Public License for more details. *
* *
* You should have received a copy of the GNU Lesser General Public *
* License along with this library; if not, write to the Free Software *
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA *
* 02110-1301 USA *
* *
* Alternatively, this file is available under the Mozilla Public *
* License Version 1.1. You may obtain a copy of the License at *
* http://www.mozilla.org/MPL/ *
***************************************************************************/
#include "dsdiffdiintag.h"
#include "tstringlist.h"
#include "tpropertymap.h"
using namespace TagLib;
using namespace DSDIFF::DIIN;
class DSDIFF::DIIN::Tag::TagPrivate
{
public:
TagPrivate()
{
}
String title;
String artist;
};
DSDIFF::DIIN::Tag::Tag() : TagLib::Tag()
{
d = new TagPrivate;
}
DSDIFF::DIIN::Tag::~Tag()
{
delete d;
}
String DSDIFF::DIIN::Tag::title() const
{
return d->title;
}
String DSDIFF::DIIN::Tag::artist() const
{
return d->artist;
}
String DSDIFF::DIIN::Tag::album() const
{
return String();
}
String DSDIFF::DIIN::Tag::comment() const
{
return String();
}
String DSDIFF::DIIN::Tag::genre() const
{
return String();
}
unsigned int DSDIFF::DIIN::Tag::year() const
{
return 0;
}
unsigned int DSDIFF::DIIN::Tag::track() const
{
return 0;
}
void DSDIFF::DIIN::Tag::setTitle(const String &title)
{
if(title.isNull() || title.isEmpty())
d->title = String();
else
d->title = title;
}
void DSDIFF::DIIN::Tag::setArtist(const String &artist)
{
if(artist.isNull() || artist.isEmpty())
d->artist = String();
else
d->artist = artist;
}
void DSDIFF::DIIN::Tag::setAlbum(const String &)
{
}
void DSDIFF::DIIN::Tag::setComment(const String &)
{
}
void DSDIFF::DIIN::Tag::setGenre(const String &)
{
}
void DSDIFF::DIIN::Tag::setYear(unsigned int)
{
}
void DSDIFF::DIIN::Tag::setTrack(unsigned int)
{
}
PropertyMap DSDIFF::DIIN::Tag::properties() const
{
PropertyMap properties;
properties["TITLE"] = d->title;
properties["ARTIST"] = d->artist;
return properties;
}
PropertyMap DSDIFF::DIIN::Tag::setProperties(const PropertyMap &origProps)
{
PropertyMap properties(origProps);
properties.removeEmpty();
StringList oneValueSet;
if(properties.contains("TITLE")) {
d->title = properties["TITLE"].front();
oneValueSet.append("TITLE");
} else
d->title = String();
if(properties.contains("ARTIST")) {
d->artist = properties["ARTIST"].front();
oneValueSet.append("ARTIST");
} else
d->artist = String();
// for each tag that has been set above, remove the first entry in the corresponding
// value list. The others will be returned as unsupported by this format.
for(StringList::Iterator it = oneValueSet.begin(); it != oneValueSet.end(); ++it) {
if(properties[*it].size() == 1)
properties.erase(*it);
else
properties[*it].erase(properties[*it].begin());
}
return properties;
}

150
3rdparty/taglib/dsdiff/dsdiffdiintag.h vendored Normal file
View File

@@ -0,0 +1,150 @@
/***************************************************************************
copyright : (C) 2016 by Damien Plisson, Audirvana
email : damien78@audirvana.com
***************************************************************************/
/***************************************************************************
* This library is free software; you can redistribute it and/or modify *
* it under the terms of the GNU Lesser General Public License version *
* 2.1 as published by the Free Software Foundation. *
* *
* This library is distributed in the hope that it will be useful, but *
* WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
* Lesser General Public License for more details. *
* *
* You should have received a copy of the GNU Lesser General Public *
* License along with this library; if not, write to the Free Software *
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA *
* 02110-1301 USA *
* *
* Alternatively, this file is available under the Mozilla Public *
* License Version 1.1. You may obtain a copy of the License at *
* http://www.mozilla.org/MPL/ *
***************************************************************************/
#ifndef TAGLIB_DSDIFFDIINTAG_H
#define TAGLIB_DSDIFFDIINTAG_H
#include "tag.h"
namespace TagLib {
namespace DSDIFF {
namespace DIIN {
/*!
* Tags from the Edited Master Chunk Info
*
* Only Title and Artist tags are supported
*/
class TAGLIB_EXPORT Tag : public TagLib::Tag
{
public:
Tag();
virtual ~Tag();
/*!
* Returns the track name; if no track name is present in the tag
* String() will be returned.
*/
String title() const;
/*!
* Returns the artist name; if no artist name is present in the tag
* String() will be returned.
*/
String artist() const;
/*!
* Not supported. Therefore always returns String().
*/
String album() const;
/*!
* Not supported. Therefore always returns String().
*/
String comment() const;
/*!
* Not supported. Therefore always returns String().
*/
String genre() const;
/*!
* Not supported. Therefore always returns 0.
*/
unsigned int year() const;
/*!
* Not supported. Therefore always returns 0.
*/
unsigned int track() const;
/*!
* Sets the title to \a title. If \a title is String() then this
* value will be cleared.
*/
void setTitle(const String &title);
/*!
* Sets the artist to \a artist. If \a artist is String() then this
* value will be cleared.
*/
void setArtist(const String &artist);
/*!
* Not supported and therefore ignored.
*/
void setAlbum(const String &album);
/*!
* Not supported and therefore ignored.
*/
void setComment(const String &comment);
/*!
* Not supported and therefore ignored.
*/
void setGenre(const String &genre);
/*!
* Not supported and therefore ignored.
*/
void setYear(unsigned int year);
/*!
* Not supported and therefore ignored.
*/
void setTrack(unsigned int track);
/*!
* Implements the unified property interface -- export function.
* Since the DIIN tag is very limited, the exported map is as well.
*/
PropertyMap properties() const;
/*!
* Implements the unified property interface -- import function.
* Because of the limitations of the DIIN file tag, any tags besides
* TITLE and ARTIST, will be
* returned. Additionally, if the map contains tags with multiple values,
* all but the first will be contained in the returned map of unsupported
* properties.
*/
PropertyMap setProperties(const PropertyMap &);
private:
Tag(const Tag &);
Tag &operator=(const Tag &);
class TagPrivate;
TagPrivate *d;
};
}
}
}
#endif

812
3rdparty/taglib/dsdiff/dsdifffile.cpp vendored Normal file
View File

@@ -0,0 +1,812 @@
/***************************************************************************
copyright : (C) 2016 by Damien Plisson, Audirvana
email : damien78@audirvana.com
***************************************************************************/
/***************************************************************************
* This library is free software; you can redistribute it and/or modify *
* it under the terms of the GNU Lesser General Public License version *
* 2.1 as published by the Free Software Foundation. *
* *
* This library is distributed in the hope that it will be useful, but *
* WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
* Lesser General Public License for more details. *
* *
* You should have received a copy of the GNU Lesser General Public *
* License along with this library; if not, write to the Free Software *
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA *
* 02110-1301 USA *
* *
* Alternatively, this file is available under the Mozilla Public *
* License Version 1.1. You may obtain a copy of the License at *
* http://www.mozilla.org/MPL/ *
***************************************************************************/
#include <tbytevector.h>
#include <tdebug.h>
#include <id3v2tag.h>
#include <tstringlist.h>
#include <tpropertymap.h>
#include <tagutils.h>
#include "tagunion.h"
#include "dsdifffile.h"
using namespace TagLib;
struct Chunk64
{
ByteVector name;
unsigned long long offset;
unsigned long long size;
char padding;
};
namespace
{
enum {
ID3v2Index = 0,
DIINIndex = 1
};
enum {
PROPChunk = 0,
DIINChunk = 1
};
}
class DSDIFF::File::FilePrivate
{
public:
FilePrivate() :
endianness(BigEndian),
size(0),
isID3InPropChunk(false),
duplicateID3V2chunkIndex(-1),
properties(0),
id3v2TagChunkID("ID3 "),
hasID3v2(false),
hasDiin(false)
{
childChunkIndex[ID3v2Index] = -1;
childChunkIndex[DIINIndex] = -1;
}
~FilePrivate()
{
delete properties;
}
Endianness endianness;
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.
Properties *properties;
TagUnion tag;
ByteVector id3v2TagChunkID;
bool hasID3v2;
bool hasDiin;
};
////////////////////////////////////////////////////////////////////////////////
// static members
////////////////////////////////////////////////////////////////////////////////
bool DSDIFF::File::isSupported(IOStream *stream)
{
// A DSDIFF file has to start with "FRM8????????DSD ".
const ByteVector id = Utils::readHeader(stream, 16, false);
return (id.startsWith("FRM8") && id.containsAt("DSD ", 12));
}
////////////////////////////////////////////////////////////////////////////////
// public members
////////////////////////////////////////////////////////////////////////////////
DSDIFF::File::File(FileName file, bool readProperties,
Properties::ReadStyle propertiesStyle) : TagLib::File(file)
{
d = new FilePrivate;
d->endianness = BigEndian;
if(isOpen())
read(readProperties, propertiesStyle);
}
DSDIFF::File::File(IOStream *stream, bool readProperties,
Properties::ReadStyle propertiesStyle) : TagLib::File(stream)
{
d = new FilePrivate;
d->endianness = BigEndian;
if(isOpen())
read(readProperties, propertiesStyle);
}
DSDIFF::File::~File()
{
delete d;
}
TagLib::Tag *DSDIFF::File::tag() const
{
return &d->tag;
}
ID3v2::Tag *DSDIFF::File::ID3v2Tag() const
{
return d->tag.access<ID3v2::Tag>(ID3v2Index, false);
}
bool DSDIFF::File::hasID3v2Tag() const
{
return d->hasID3v2;
}
DSDIFF::DIIN::Tag *DSDIFF::File::DIINTag() const
{
return d->tag.access<DSDIFF::DIIN::Tag>(DIINIndex, false);
}
bool DSDIFF::File::hasDIINTag() const
{
return d->hasDiin;
}
PropertyMap DSDIFF::File::properties() const
{
if(d->hasID3v2)
return d->tag.access<ID3v2::Tag>(ID3v2Index, false)->properties();
return PropertyMap();
}
void DSDIFF::File::removeUnsupportedProperties(const StringList &unsupported)
{
if(d->hasID3v2)
d->tag.access<ID3v2::Tag>(ID3v2Index, false)->removeUnsupportedProperties(unsupported);
if(d->hasDiin)
d->tag.access<DSDIFF::DIIN::Tag>(DIINIndex, false)->removeUnsupportedProperties(unsupported);
}
PropertyMap DSDIFF::File::setProperties(const PropertyMap &properties)
{
return d->tag.access<ID3v2::Tag>(ID3v2Index, true)->setProperties(properties);
}
DSDIFF::Properties *DSDIFF::File::audioProperties() const
{
return d->properties;
}
bool DSDIFF::File::save()
{
if(readOnly()) {
debug("DSDIFF::File::save() -- File is read only.");
return false;
}
if(!isValid()) {
debug("DSDIFF::File::save() -- Trying to save invalid file.");
return false;
}
// 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;
}
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;
}
}
// 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()) {
ByteVector diinTitle;
diinTitle.append(ByteVector::fromUInt(diinTag->title().size(), d->endianness == BigEndian));
diinTitle.append(ByteVector::fromCString(diinTag->title().toCString()));
setChildChunkData("DITI", diinTitle, DIINChunk);
}
else
setChildChunkData("DITI", ByteVector(), DIINChunk);
if(!diinTag->artist().isNull() && !diinTag->artist().isEmpty()) {
ByteVector diinArtist;
diinArtist.append(ByteVector::fromUInt(diinTag->artist().size(), d->endianness == BigEndian));
diinArtist.append(ByteVector::fromCString(diinTag->artist().toCString()));
setChildChunkData("DIAR", diinArtist, DIINChunk);
}
else
setChildChunkData("DIAR", ByteVector(), DIINChunk);
}
// Third: remove the duplicate ID3V2 chunk (inside PROP chunk) if any
if(d->duplicateID3V2chunkIndex>=0) {
setChildChunkData(d->duplicateID3V2chunkIndex, ByteVector(), PROPChunk);
d->duplicateID3V2chunkIndex = -1;
}
return true;
}
////////////////////////////////////////////////////////////////////////////////
// private members
////////////////////////////////////////////////////////////////////////////////
void DSDIFF::File::setRootChunkData(unsigned int i, const ByteVector &data)
{
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;
insert(ByteVector::fromLongLong(d->size, d->endianness == BigEndian), 4, 8);
removeBlock(d->chunks[i].offset - 12, removedChunkTotalSize);
// 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);
}
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);
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)
{
if(d->chunks.size() == 0) {
debug("DSDIFF::File::setPropChunkData - No valid chunks found.");
return;
}
for(unsigned int i = 0; i < d->chunks.size(); i++) {
if(d->chunks[i].name == name) {
setRootChunkData(i, data);
return;
}
}
// Couldn't find an existing chunk, so let's create a new one.
unsigned int 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
d->size += (offset & 1) + ((data.size() + 1) & ~1) + 12;
insert(ByteVector::fromLongLong(d->size, d->endianness == BigEndian), 4, 8);
// Now add the chunk to the file
writeChunk(name,
data,
offset,
std::max<unsigned long long>(0, length() - offset),
(offset & 1) ? 1 : 0);
Chunk64 chunk;
chunk.name = name;
chunk.size = data.size();
chunk.offset = offset + 12;
chunk.padding = (data.size() & 0x01) ? 1 : 0;
d->chunks.push_back(chunk);
}
void DSDIFF::File::setChildChunkData(unsigned int i,
const ByteVector &data,
unsigned int childChunkNum)
{
std::vector<Chunk64> &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++;
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
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);
}
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);
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];
if(childChunks.size() == 0) {
debug("DSDIFF::File::setPropChunkData - No valid chunks found.");
return;
}
for(unsigned int i = 0; i < childChunks.size(); i++) {
if(childChunks[i].name == name) {
setChildChunkData(i, data, childChunkNum);
return;
}
}
// Do not attempt to remove a non existing chunk
if(data.isNull() || 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,
d->endianness == BigEndian),
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;
writeChunk(name, data, offset,
std::max<unsigned long long>(0, nextRootChunkIdx - offset),
(offset & 1) ? 1 : 0);
// For root chunks
updateRootChunksStructure(d->childChunkIndex[childChunkNum] + 1);
Chunk64 chunk;
chunk.name = name;
chunk.size = data.size();
chunk.offset = offset + 12;
chunk.padding = (data.size() & 0x01) ? 1 : 0;
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++)
d->chunks[i].offset = d->chunks[i - 1].offset + 12
+ 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];
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];
if(childChunksToUpdate.size() > 0) {
childChunksToUpdate[0].offset = d->chunks[d->childChunkIndex[DIINChunk]].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;
}
}
}
void DSDIFF::File::read(bool readProperties, Properties::ReadStyle propertiesStyle)
{
bool bigEndian = (d->endianness == BigEndian);
d->type = readBlock(4);
d->size = readBlock(8).toLongLong(bigEndian);
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);
if(!isValidChunkID(chunkName)) {
debug("DSDIFF::File::read() -- Chunk '" + chunkName + "' has invalid ID");
setValid(false);
break;
}
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);
break;
}
Chunk64 chunk;
chunk.name = chunkName;
chunk.size = chunkSize;
chunk.offset = tell();
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))
// Not well formed, re-seek
seek(uPosNotPadded, Beginning);
else
chunk.padding = 1;
}
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(unsigned int i = 0; i < d->chunks.size(); i++) {
if(d->chunks[i].name == "DSD ") {
lengthDSDSamplesTimeChannels = d->chunks[i].size * 8;
audioDataSizeinBytes = d->chunks[i].size;
}
else if(d->chunks[i].name == "DST ") {
// Now decode the chunks inside the DST chunk to read the DST Frame Information one
long long dstChunkEnd = d->chunks[i].offset + d->chunks[i].size;
seek(d->chunks[i].offset);
audioDataSizeinBytes = d->chunks[i].size;
while(tell() + 12 <= dstChunkEnd) {
ByteVector dstChunkName = readBlock(4);
long long dstChunkSize = readBlock(8).toLongLong(bigEndian);
if(!isValidChunkID(dstChunkName)) {
debug("DSDIFF::File::read() -- DST Chunk '" + dstChunkName + "' has invalid ID");
setValid(false);
break;
}
if(static_cast<long long>(tell()) + dstChunkSize > dstChunkEnd) {
debug("DSDIFF::File::read() -- DST Chunk '" + dstChunkName
+ "' has invalid size (larger than the DST chunk)");
setValid(false);
break;
}
if(dstChunkName == "FRTE") {
// 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
}
seek(dstChunkSize, Current);
// Check padding
long uPosNotPadded = tell();
if((uPosNotPadded & 0x01) != 0) {
ByteVector iByte = readBlock(1);
if((iByte.size() != 1) || (iByte[0] != 0))
// Not well formed, re-seek
seek(uPosNotPadded, Beginning);
}
}
}
else if(d->chunks[i].name == "PROP") {
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
while(tell() + 12 <= propChunkEnd) {
ByteVector propChunkName = readBlock(4);
long long propChunkSize = readBlock(8).toLongLong(bigEndian);
if(!isValidChunkID(propChunkName)) {
debug("DSDIFF::File::read() -- PROP Chunk '" + propChunkName + "' has invalid ID");
setValid(false);
break;
}
if(static_cast<long long>(tell()) + propChunkSize > propChunkEnd) {
debug("DSDIFF::File::read() -- PROP Chunk '" + propChunkName
+ "' has invalid size (larger than the PROP chunk)");
setValid(false);
break;
}
Chunk64 chunk;
chunk.name = propChunkName;
chunk.size = propChunkSize;
chunk.offset = tell();
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))
// Not well formed, re-seek
seek(uPosNotPadded, Beginning);
else
chunk.padding = 1;
}
d->childChunks[PROPChunk].push_back(chunk);
}
}
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);
while(tell() + 12 <= diinChunkEnd) {
ByteVector diinChunkName = readBlock(4);
long long diinChunkSize = readBlock(8).toLongLong(bigEndian);
if(!isValidChunkID(diinChunkName)) {
debug("DSDIFF::File::read() -- DIIN Chunk '" + diinChunkName + "' has invalid ID");
setValid(false);
break;
}
if(static_cast<long long>(tell()) + diinChunkSize > diinChunkEnd) {
debug("DSDIFF::File::read() -- DIIN Chunk '" + diinChunkName
+ "' has invalid size (larger than the DIIN chunk)");
setValid(false);
break;
}
Chunk64 chunk;
chunk.name = diinChunkName;
chunk.size = diinChunkSize;
chunk.offset = tell();
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))
// Not well formed, re-seek
seek(uPosNotPadded, Beginning);
else
chunk.padding = 1;
}
d->childChunks[DIINChunk].push_back(chunk);
}
}
else if(d->chunks[i].name == "ID3 " || d->chunks[i].name == "id3 ") {
d->id3v2TagChunkID = d->chunks[i].name;
d->tag.set(ID3v2Index, new ID3v2::Tag(this, d->chunks[i].offset));
d->isID3InPropChunk = false;
d->hasID3v2 = true;
}
}
if(!isValid())
return;
if(d->childChunkIndex[PROPChunk] < 0) {
debug("DSDIFF::File::read() -- no PROP chunk found");
setValid(false);
return;
}
// Read properties
unsigned int sampleRate=0;
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->hasID3v2) {
d->duplicateID3V2chunkIndex = i;
continue; // ID3V2 tag has already been found at root level
}
d->id3v2TagChunkID = d->childChunks[PROPChunk][i].name;
d->tag.set(ID3v2Index, new ID3v2::Tag(this, d->childChunks[PROPChunk][i].offset));
d->isID3InPropChunk = true;
d->hasID3v2 = true;
}
else if(d->childChunks[PROPChunk][i].name == "FS ") {
// Sample rate
seek(d->childChunks[PROPChunk][i].offset);
sampleRate = readBlock(4).toUInt(0, 4, bigEndian);
}
else if(d->childChunks[PROPChunk][i].name == "CHNL") {
// Channels
seek(d->childChunks[PROPChunk][i].offset);
channels = readBlock(2).toShort(0, bigEndian);
}
}
// Read title & artist from DIIN chunk
d->tag.access<DSDIFF::DIIN::Tag>(DIINIndex, true);
if(d->hasDiin) {
for(unsigned int i = 0; i < d->childChunks[DIINChunk].size(); i++) {
if(d->childChunks[DIINChunk][i].name == "DITI") {
seek(d->childChunks[DIINChunk][i].offset);
unsigned int titleStrLength = readBlock(4).toUInt(0, 4, bigEndian);
if(titleStrLength <= d->childChunks[DIINChunk][i].size) {
ByteVector titleStr = readBlock(titleStrLength);
d->tag.access<DSDIFF::DIIN::Tag>(DIINIndex, false)->setTitle(titleStr);
}
}
else if(d->childChunks[DIINChunk][i].name == "DIAR") {
seek(d->childChunks[DIINChunk][i].offset);
unsigned int artistStrLength = readBlock(4).toUInt(0, 4, bigEndian);
if(artistStrLength <= d->childChunks[DIINChunk][i].size) {
ByteVector artistStr = readBlock(artistStrLength);
d->tag.access<DSDIFF::DIIN::Tag>(DIINIndex, false)->setArtist(artistStr);
}
}
}
}
if(readProperties) {
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;
else
lengthDSDSamplesTimeChannels = 0;
}
else {
// In DSD uncompressed files, the read number of samples is the total for each channel
if(channels > 0)
lengthDSDSamplesTimeChannels /= channels;
}
int bitrate = 0;
if(lengthDSDSamplesTimeChannels > 0)
bitrate = (audioDataSizeinBytes*8*sampleRate) / lengthDSDSamplesTimeChannels / 1000;
d->properties = new Properties(sampleRate,
channels,
lengthDSDSamplesTimeChannels,
bitrate,
propertiesStyle);
}
if(!ID3v2Tag()) {
d->tag.access<ID3v2::Tag>(ID3v2Index, true);
d->isID3InPropChunk = false; // By default, ID3 chunk is at root level
d->hasID3v2 = false;
}
}
void DSDIFF::File::writeChunk(const ByteVector &name, const ByteVector &data,
unsigned long long offset, unsigned long replace,
unsigned int leadingPadding)
{
ByteVector combined;
if(leadingPadding)
combined.append(ByteVector(leadingPadding, '\x00'));
combined.append(name);
combined.append(ByteVector::fromLongLong(data.size(), d->endianness == BigEndian));
combined.append(data);
if((data.size() & 0x01) != 0)
combined.append('\x00');
insert(combined, offset, replace);
}

260
3rdparty/taglib/dsdiff/dsdifffile.h vendored Normal file
View File

@@ -0,0 +1,260 @@
/***************************************************************************
copyright : (C) 2016 by Damien Plisson, Audirvana
email : damien78@audirvana.com
***************************************************************************/
/***************************************************************************
* This library is free software; you can redistribute it and/or modify *
* it under the terms of the GNU Lesser General Public License version *
* 2.1 as published by the Free Software Foundation. *
* *
* This library is distributed in the hope that it will be useful, but *
* WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
* Lesser General Public License for more details. *
* *
* You should have received a copy of the GNU Lesser General Public *
* License along with this library; if not, write to the Free Software *
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA *
* 02110-1301 USA *
* *
* Alternatively, this file is available under the Mozilla Public *
* License Version 1.1. You may obtain a copy of the License at *
* http://www.mozilla.org/MPL/ *
***************************************************************************/
#ifndef TAGLIB_DSDIFFFILE_H
#define TAGLIB_DSDIFFFILE_H
#include "rifffile.h"
#include "id3v2tag.h"
#include "dsdiffproperties.h"
#include "dsdiffdiintag.h"
namespace TagLib {
//! An implementation of DSDIFF metadata
/*!
* This is implementation of DSDIFF metadata.
*
* 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
* at http://dsd-guide.com/sites/default/files/white-papers/DSDIFF_1.5_Spec.pdf
* DSDIFF standard does not explictly 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
*/
namespace DSDIFF {
//! An implementation of TagLib::File with DSDIFF specific methods
/*!
* This implements and provides an interface for DSDIFF files to the
* TagLib::Tag and TagLib::AudioProperties interfaces by way of implementing
* the abstract TagLib::File API as well as providing some additional
* information specific to DSDIFF files.
*/
class TAGLIB_EXPORT File : public TagLib::File
{
public:
/*!
* Constructs an DSDIFF file from \a file. If \a readProperties is true
* the file's audio properties will also be read.
*
* \note In the current implementation, \a propertiesStyle is ignored.
*/
File(FileName file, bool readProperties = true,
Properties::ReadStyle propertiesStyle = Properties::Average);
/*!
* Constructs an DSDIFF file from \a stream. If \a readProperties is true
* the file's audio properties will also be read.
*
* \note TagLib will *not* take ownership of the stream, the caller is
* responsible for deleting it after the File object.
*
* \note In the current implementation, \a propertiesStyle is ignored.
*/
File(IOStream *stream, bool readProperties = true,
Properties::ReadStyle propertiesStyle = Properties::Average);
/*!
* Destroys this instance of the File.
*/
virtual ~File();
/*!
* Returns a pointer to a tag that is the union of the ID3v2 and DIIN
* tags. The ID3v2 tag is given priority in reading the information -- if
* requested information exists in both the ID3v2 tag and the ID3v1 tag,
* the information from the ID3v2 tag will be returned.
*
* If you would like more granular control over the content of the tags,
* with the concession of generality, use the tag-type specific calls.
*
* \note As this tag is not implemented as an ID3v2 tag or a DIIN tag,
* but a union of the two this pointer may not be cast to the specific
* tag types.
*
* \see ID3v2Tag()
* \see DIINTag()
*/
virtual Tag *tag() const;
/*!
* Returns the ID3V2 Tag for this file.
*
* \note This always returns a valid pointer regardless of whether or not
* the file on disk has an ID3v2 tag. Use hasID3v2Tag() to check if the
* file on disk actually has an ID3v2 tag.
*
* \see hasID3v2Tag()
*/
virtual ID3v2::Tag *ID3v2Tag() const;
/*!
* Returns the DSDIFF DIIN Tag for this file
*
*/
DSDIFF::DIIN::Tag *DIINTag() const;
/*!
* Implements the unified property interface -- export function.
* This method forwards to ID3v2::Tag::properties().
*/
PropertyMap properties() const;
void removeUnsupportedProperties(const StringList &properties);
/*!
* Implements the unified property interface -- import function.
* This method forwards to ID3v2::Tag::setProperties().
*/
PropertyMap setProperties(const PropertyMap &);
/*!
* Returns the AIFF::Properties for this file. If no audio properties
* were read then this will return a null pointer.
*/
virtual Properties *audioProperties() const;
/*!
* Save the file. If at least one tag -- ID3v1 or DIIN -- exists this
* will duplicate its content into the other tag. This returns true
* if saving was successful.
*
* If neither exists or if both tags are empty, this will strip the tags
* from the file.
*
* This is the same as calling save(AllTags);
*
* If you would like more granular control over the content of the tags,
* with the concession of generality, use paramaterized save call below.
*
* \see save(int tags)
*/
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.
*/
bool save(int tags);
/*!
* Returns whether or not the file on disk actually has an ID3v2 tag.
*
* \see ID3v2Tag()
*/
bool hasID3v2Tag() const;
/*!
* Returns whether or not the file on disk actually has the DSDIFF
* Title & Artist tag.
*
* \see DIINTag()
*/
bool hasDIINTag() const;
/*!
* Returns whether or not the given \a stream can be opened as a DSDIFF
* file.
*
* \note This method is designed to do a quick check. The result may
* not necessarily be correct.
*/
static bool isSupported(IOStream *stream);
protected:
enum Endianness { BigEndian, LittleEndian };
File(FileName file, Endianness endianness);
File(IOStream *stream, Endianness endianness);
private:
File(const File &);
File &operator=(const File &);
/*!
* Sets the data for the the specified chunk at root level to \a data.
*
* \warning This will update the file immediately.
*/
void setRootChunkData(unsigned int i, const ByteVector &data);
/*!
* Sets the data for the root-level chunk \a name to \a data.
* If a root-level chunk with the given name already exists
* it will be overwritten, otherwise it will be
* created after the existing chunks.
*
* \warning This will update the file immediately.
*/
void setRootChunkData(const ByteVector &name, const ByteVector &data);
/*!
* Sets the data for the the specified child chunk to \a data.
*
* If data is null, then remove the chunk
*
* \warning This will update the file immediately.
*/
void setChildChunkData(unsigned int i, const ByteVector &data,
unsigned int childChunkNum);
/*!
* Sets the data for the child chunk \a name to \a data. If a chunk with
* the given name already exists it will be overwritten, otherwise it will
* be created after the existing chunks inside child chunk.
*
* If data is null, then remove the chunks with \a name name
*
* \warning This will update the file immediately.
*/
void setChildChunkData(const ByteVector &name, const ByteVector &data,
unsigned int childChunkNum);
void updateRootChunksStructure(unsigned int startingChunk);
void read(bool readProperties, Properties::ReadStyle propertiesStyle);
void writeChunk(const ByteVector &name, const ByteVector &data,
unsigned long long offset, unsigned long replace = 0,
unsigned int leadingPadding = 0);
class FilePrivate;
FilePrivate *d;
};
}
}
#endif

View File

@@ -0,0 +1,120 @@
/***************************************************************************
copyright : (C) 2016 by Damien Plisson, Audirvana
email : damien78@audirvana.com
***************************************************************************/
/***************************************************************************
* This library is free software; you can redistribute it and/or modify *
* it under the terms of the GNU Lesser General Public License version *
* 2.1 as published by the Free Software Foundation. *
* *
* This library is distributed in the hope that it will be useful, but *
* WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
* Lesser General Public License for more details. *
* *
* You should have received a copy of the GNU Lesser General Public *
* License along with this library; if not, write to the Free Software *
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA *
* 02110-1301 USA *
* *
* Alternatively, this file is available under the Mozilla Public *
* License Version 1.1. You may obtain a copy of the License at *
* http://www.mozilla.org/MPL/ *
***************************************************************************/
#include <tstring.h>
#include <tdebug.h>
#include "dsdiffproperties.h"
using namespace TagLib;
class DSDIFF::Properties::PropertiesPrivate
{
public:
PropertiesPrivate() :
length(0),
bitrate(0),
sampleRate(0),
channels(0),
sampleWidth(0),
sampleCount(0)
{
}
int length;
int bitrate;
int sampleRate;
int channels;
int sampleWidth;
unsigned long long sampleCount;
};
////////////////////////////////////////////////////////////////////////////////
// public members
////////////////////////////////////////////////////////////////////////////////
DSDIFF::Properties::Properties(const unsigned int sampleRate,
const unsigned short channels,
const unsigned long long samplesCount,
const int bitrate,
ReadStyle style) : AudioProperties(style)
{
d = new PropertiesPrivate;
d->channels = channels;
d->sampleCount = samplesCount;
d->sampleWidth = 1;
d->sampleRate = sampleRate;
d->bitrate = bitrate;
d->length = d->sampleRate > 0
? static_cast<int>((d->sampleCount * 1000.0) / d->sampleRate + 0.5)
: 0;
}
DSDIFF::Properties::~Properties()
{
delete d;
}
int DSDIFF::Properties::length() const
{
return lengthInSeconds();
}
int DSDIFF::Properties::lengthInSeconds() const
{
return d->length / 1000;
}
int DSDIFF::Properties::lengthInMilliseconds() const
{
return d->length;
}
int DSDIFF::Properties::bitrate() const
{
return d->bitrate;
}
int DSDIFF::Properties::sampleRate() const
{
return d->sampleRate;
}
int DSDIFF::Properties::channels() const
{
return d->channels;
}
int DSDIFF::Properties::bitsPerSample() const
{
return d->sampleWidth;
}
long long DSDIFF::Properties::sampleCount() const
{
return d->sampleCount;
}

View File

@@ -0,0 +1,83 @@
/***************************************************************************
copyright : (C) 2016 by Damien Plisson, Audirvana
email : damien78@audirvana.com
***************************************************************************/
/***************************************************************************
* This library is free software; you can redistribute it and/or modify *
* it under the terms of the GNU Lesser General Public License version *
* 2.1 as published by the Free Software Foundation. *
* *
* This library is distributed in the hope that it will be useful, but *
* WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
* Lesser General Public License for more details. *
* *
* You should have received a copy of the GNU Lesser General Public *
* License along with this library; if not, write to the Free Software *
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA *
* 02110-1301 USA *
* *
* Alternatively, this file is available under the Mozilla Public *
* License Version 1.1. You may obtain a copy of the License at *
* http://www.mozilla.org/MPL/ *
***************************************************************************/
#ifndef TAGLIB_DSDIFFPROPERTIES_H
#define TAGLIB_DSDIFFPROPERTIES_H
#include "audioproperties.h"
namespace TagLib {
namespace DSDIFF {
class File;
//! An implementation of audio property reading for DSDIFF
/*!
* This reads the data from an DSDIFF stream found in the AudioProperties
* API.
*/
class TAGLIB_EXPORT Properties : public AudioProperties
{
public:
/*!
* Create an instance of DSDIFF::Properties with the data read from the
* ByteVector \a data.
*/
Properties(const unsigned int sampleRate, const unsigned short channels,
const unsigned long long samplesCount, const int bitrate,
ReadStyle style);
/*!
* Destroys this DSDIFF::Properties instance.
*/
virtual ~Properties();
// Reimplementations.
virtual int length() const;
virtual int lengthInSeconds() const;
virtual int lengthInMilliseconds() const;
virtual int bitrate() const;
virtual int sampleRate() const;
virtual int channels() const;
int bitsPerSample() const;
long long sampleCount() const;
private:
Properties(const Properties &);
Properties &operator=(const Properties &);
class PropertiesPrivate;
PropertiesPrivate *d;
};
}
}
#endif

241
3rdparty/taglib/dsf/dsffile.cpp vendored Normal file
View File

@@ -0,0 +1,241 @@
/***************************************************************************
copyright : (C) 2013 - 2018 by Stephen F. Booth
email : me@sbooth.org
***************************************************************************/
/***************************************************************************
* This library is free software; you can redistribute it and/or modify *
* it under the terms of the GNU Lesser General Public License version *
* 2.1 as published by the Free Software Foundation. *
* *
* This library is distributed in the hope that it will be useful, but *
* WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
* Lesser General Public License for more details. *
* *
* You should have received a copy of the GNU Lesser General Public *
* License along with this library; if not, write to the Free Software *
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA *
* 02110-1301 USA *
* *
* Alternatively, this file is available under the Mozilla Public *
* License Version 1.1. You may obtain a copy of the License at *
* http://www.mozilla.org/MPL/ *
***************************************************************************/
#include <tbytevector.h>
#include <tdebug.h>
#include <id3v2tag.h>
#include <tstringlist.h>
#include <tpropertymap.h>
#include <tagutils.h>
#include "dsffile.h"
using namespace TagLib;
// The DSF specification is located at http://dsd-guide.com/sites/default/files/white-papers/DSFFileFormatSpec_E.pdf
class DSF::File::FilePrivate
{
public:
FilePrivate() :
properties(0),
tag(0)
{
}
~FilePrivate()
{
delete properties;
delete tag;
}
long long fileSize;
long long metadataOffset;
Properties *properties;
ID3v2::Tag *tag;
};
////////////////////////////////////////////////////////////////////////////////
// static members
////////////////////////////////////////////////////////////////////////////////
bool DSF::File::isSupported(IOStream *stream)
{
// A DSF file has to start with "DSD "
const ByteVector id = Utils::readHeader(stream, 4, false);
return id.startsWith("DSD ");
}
////////////////////////////////////////////////////////////////////////////////
// public members
////////////////////////////////////////////////////////////////////////////////
DSF::File::File(FileName file, bool readProperties,
Properties::ReadStyle propertiesStyle) :
TagLib::File(file),
d(new FilePrivate())
{
if(isOpen())
read(readProperties, propertiesStyle);
}
DSF::File::File(IOStream *stream, bool readProperties,
Properties::ReadStyle propertiesStyle) :
TagLib::File(stream),
d(new FilePrivate())
{
if(isOpen())
read(readProperties, propertiesStyle);
}
DSF::File::~File()
{
delete d;
}
ID3v2::Tag *DSF::File::tag() const
{
return d->tag;
}
PropertyMap DSF::File::properties() const
{
return d->tag->properties();
}
PropertyMap DSF::File::setProperties(const PropertyMap &properties)
{
return d->tag->setProperties(properties);
}
DSF::Properties *DSF::File::audioProperties() const
{
return d->properties;
}
bool DSF::File::save()
{
if(readOnly()) {
debug("DSF::File::save() -- File is read only.");
return false;
}
if(!isValid()) {
debug("DSF::File::save() -- Trying to save invalid file.");
return false;
}
// Three things must be updated: the file size, the tag data, and the metadata offset
if(d->tag->isEmpty()) {
long long newFileSize = d->metadataOffset ? d->metadataOffset : d->fileSize;
// Update the file size
if(d->fileSize != newFileSize) {
insert(ByteVector::fromLongLong(newFileSize, false), 12, 8);
d->fileSize = newFileSize;
}
// Update the metadata offset to 0 since there is no longer a tag
if(d->metadataOffset) {
insert(ByteVector::fromLongLong(0ULL, false), 20, 8);
d->metadataOffset = 0;
}
// Delete the old tag
truncate(newFileSize);
}
else {
ByteVector tagData = d->tag->render();
long long newMetadataOffset = d->metadataOffset ? d->metadataOffset : d->fileSize;
long long newFileSize = newMetadataOffset + tagData.size();
long long oldTagSize = d->fileSize - newMetadataOffset;
// Update the file size
if(d->fileSize != newFileSize) {
insert(ByteVector::fromLongLong(newFileSize, false), 12, 8);
d->fileSize = newFileSize;
}
// Update the metadata offset
if(d->metadataOffset != newMetadataOffset) {
insert(ByteVector::fromLongLong(newMetadataOffset, false), 20, 8);
d->metadataOffset = newMetadataOffset;
}
// Delete the old tag and write the new one
insert(tagData, newMetadataOffset, static_cast<size_t>(oldTagSize));
}
return true;
}
////////////////////////////////////////////////////////////////////////////////
// private members
////////////////////////////////////////////////////////////////////////////////
void DSF::File::read(bool readProperties, Properties::ReadStyle propertiesStyle)
{
// A DSF file consists of four chunks: DSD chunk, format chunk, data chunk, and metadata chunk
// The file format is not chunked in the sense of a RIFF File, though
// DSD chunk
ByteVector chunkName = readBlock(4);
if(chunkName != "DSD ") {
debug("DSF::File::read() -- Not a DSF file.");
setValid(false);
return;
}
long long chunkSize = readBlock(8).toLongLong(false);
// Integrity check
if(28 != chunkSize) {
debug("DSF::File::read() -- File is corrupted, wrong chunk size");
setValid(false);
return;
}
d->fileSize = readBlock(8).toLongLong(false);
// File is malformed or corrupted
if(d->fileSize != length()) {
debug("DSF::File::read() -- File is corrupted wrong length");
setValid(false);
return;
}
d->metadataOffset = readBlock(8).toLongLong(false);
// File is malformed or corrupted
if(d->metadataOffset > d->fileSize) {
debug("DSF::File::read() -- Invalid metadata offset.");
setValid(false);
return;
}
// Format chunk
chunkName = readBlock(4);
if(chunkName != "fmt ") {
debug("DSF::File::read() -- Missing 'fmt ' chunk.");
setValid(false);
return;
}
chunkSize = readBlock(8).toLongLong(false);
d->properties = new Properties(readBlock(chunkSize), propertiesStyle);
// Skip the data chunk
// A metadata offset of 0 indicates the absence of an ID3v2 tag
if(0 == d->metadataOffset)
d->tag = new ID3v2::Tag();
else
d->tag = new ID3v2::Tag(this, d->metadataOffset);
}

128
3rdparty/taglib/dsf/dsffile.h vendored Normal file
View File

@@ -0,0 +1,128 @@
/***************************************************************************
copyright : (C) 2013 - 2018 by Stephen F. Booth
email : me@sbooth.org
***************************************************************************/
/***************************************************************************
* This library is free software; you can redistribute it and/or modify *
* it under the terms of the GNU Lesser General Public License version *
* 2.1 as published by the Free Software Foundation. *
* *
* This library is distributed in the hope that it will be useful, but *
* WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
* Lesser General Public License for more details. *
* *
* You should have received a copy of the GNU Lesser General Public *
* License along with this library; if not, write to the Free Software *
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA *
* 02110-1301 USA *
* *
* Alternatively, this file is available under the Mozilla Public *
* License Version 1.1. You may obtain a copy of the License at *
* http://www.mozilla.org/MPL/ *
***************************************************************************/
#ifndef TAGLIB_DSFFILE_H
#define TAGLIB_DSFFILE_H
#include "tfile.h"
#include "id3v2tag.h"
#include "dsfproperties.h"
namespace TagLib {
//! An implementation of DSF metadata
/*!
* This is implementation of DSF metadata.
*
* This supports an ID3v2 tag as well as properties from the file.
*/
namespace DSF {
//! An implementation of 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
* information specific to DSF files.
*/
class TAGLIB_EXPORT File : public TagLib::File
{
public:
/*!
* Contructs 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.
*/
File(FileName file, bool readProperties = true,
Properties::ReadStyle propertiesStyle = Properties::Average);
/*!
* Contructs 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.
*/
File(IOStream *stream, bool readProperties = true,
Properties::ReadStyle propertiesStyle = Properties::Average);
/*!
* Destroys this instance of the File.
*/
virtual ~File();
/*!
* Returns the Tag for this file.
*/
ID3v2::Tag *tag() const;
/*!
* Implements the unified property interface -- export function.
* This method forwards to ID3v2::Tag::properties().
*/
PropertyMap properties() const;
/*!
* Implements the unified property interface -- import function.
* This method forwards to ID3v2::Tag::setProperties().
*/
PropertyMap setProperties(const PropertyMap &);
/*!
* Returns the DSF::AudioProperties for this file. If no audio properties
* were read then this will return a null pointer.
*/
virtual Properties *audioProperties() const;
/*!
* Saves the file.
*/
virtual bool save();
/*!
* Returns whether or not the given \a stream can be opened as a DSF
* file.
*
* \note This method is designed to do a quick check. The result may
* not necessarily be correct.
*/
static bool isSupported(IOStream *stream);
private:
File(const File &);
File &operator=(const File &);
void read(bool readProperties, Properties::ReadStyle propertiesStyle);
class FilePrivate;
FilePrivate *d;
};
}
}
#endif

161
3rdparty/taglib/dsf/dsfproperties.cpp vendored Normal file
View File

@@ -0,0 +1,161 @@
/***************************************************************************
copyright : (C) 2013 by Stephen F. Booth
email : me@sbooth.org
***************************************************************************/
/***************************************************************************
* This library is free software; you can redistribute it and/or modify *
* it under the terms of the GNU Lesser General Public License version *
* 2.1 as published by the Free Software Foundation. *
* *
* This library is distributed in the hope that it will be useful, but *
* WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
* Lesser General Public License for more details. *
* *
* You should have received a copy of the GNU Lesser General Public *
* License along with this library; if not, write to the Free Software *
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA *
* 02110-1301 USA *
* *
* Alternatively, this file is available under the Mozilla Public *
* License Version 1.1. You may obtain a copy of the License at *
* http://www.mozilla.org/MPL/ *
***************************************************************************/
#include <tstring.h>
#include <tdebug.h>
#include "dsfproperties.h"
using namespace TagLib;
class DSF::Properties::PropertiesPrivate
{
public:
PropertiesPrivate() :
formatVersion(0),
formatID(0),
channelType(0),
channelNum(0),
samplingFrequency(0),
bitsPerSample(0),
sampleCount(0),
blockSizePerChannel(0),
bitrate(0),
length(0)
{
}
// Nomenclature is from DSF file format specification
unsigned int formatVersion;
unsigned int formatID;
unsigned int channelType;
unsigned int channelNum;
unsigned int samplingFrequency;
unsigned int bitsPerSample;
long long sampleCount;
unsigned int blockSizePerChannel;
// Computed
unsigned int bitrate;
unsigned int length;
};
////////////////////////////////////////////////////////////////////////////////
// public members
////////////////////////////////////////////////////////////////////////////////
DSF::Properties::Properties(const ByteVector &data, ReadStyle style) : TagLib::AudioProperties(style)
{
d = new PropertiesPrivate;
read(data);
}
DSF::Properties::~Properties()
{
delete d;
}
int DSF::Properties::length() const
{
return lengthInSeconds();
}
int DSF::Properties::lengthInSeconds() const
{
return d->length / 1000;
}
int DSF::Properties::lengthInMilliseconds() const
{
return d->length;
}
int DSF::Properties::bitrate() const
{
return d->bitrate;
}
int DSF::Properties::sampleRate() const
{
return d->samplingFrequency;
}
int DSF::Properties::channels() const
{
return d->channelNum;
}
// DSF specific
int DSF::Properties::formatVersion() const
{
return d->formatVersion;
}
int DSF::Properties::formatID() const
{
return d->formatID;
}
int DSF::Properties::channelType() const
{
return d->channelType;
}
int DSF::Properties::bitsPerSample() const
{
return d->bitsPerSample;
}
long long DSF::Properties::sampleCount() const
{
return d->sampleCount;
}
int DSF::Properties::blockSizePerChannel() const
{
return d->blockSizePerChannel;
}
////////////////////////////////////////////////////////////////////////////////
// private members
////////////////////////////////////////////////////////////////////////////////
void DSF::Properties::read(const ByteVector &data)
{
d->formatVersion = data.toUInt(0U,false);
d->formatID = data.toUInt(4U,false);
d->channelType = data.toUInt(8U,false);
d->channelNum = data.toUInt(12U,false);
d->samplingFrequency = data.toUInt(16U,false);
d->bitsPerSample = data.toUInt(20U,false);
d->sampleCount = data.toLongLong(24U,false);
d->blockSizePerChannel = data.toUInt(32U,false);
d->bitrate
= static_cast<unsigned int>((d->samplingFrequency * d->bitsPerSample * d->channelNum) / 1000.0 + 0.5);
d->length
= d->samplingFrequency > 0 ? static_cast<unsigned int>(d->sampleCount * 1000.0 / d->samplingFrequency + 0.5) : 0;
}

92
3rdparty/taglib/dsf/dsfproperties.h vendored Normal file
View File

@@ -0,0 +1,92 @@
/***************************************************************************
copyright : (C) 2013 by Stephen F. Booth
email : me@sbooth.org
***************************************************************************/
/***************************************************************************
* This library is free software; you can redistribute it and/or modify *
* it under the terms of the GNU Lesser General Public License version *
* 2.1 as published by the Free Software Foundation. *
* *
* This library is distributed in the hope that it will be useful, but *
* WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
* Lesser General Public License for more details. *
* *
* You should have received a copy of the GNU Lesser General Public *
* License along with this library; if not, write to the Free Software *
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA *
* 02110-1301 USA *
* *
* Alternatively, this file is available under the Mozilla Public *
* License Version 1.1. You may obtain a copy of the License at *
* http://www.mozilla.org/MPL/ *
***************************************************************************/
#ifndef TAGLIB_DSFPROPERTIES_H
#define TAGLIB_DSFPROPERTIES_H
#include "audioproperties.h"
namespace TagLib {
namespace DSF {
class File;
//! An implementation of audio property reading for DSF
/*!
* This reads the data from a DSF stream found in the AudioProperties
* API.
*/
class TAGLIB_EXPORT Properties : public TagLib::AudioProperties
{
public:
/*!
* Create an instance of DSF::AudioProperties with the data read from the
* ByteVector \a data.
*/
Properties(const ByteVector &data, ReadStyle style);
/*!
* Destroys this DSF::AudioProperties instance.
*/
virtual ~Properties();
// Reimplementations.
virtual int length() const;
virtual int lengthInSeconds() const;
virtual int lengthInMilliseconds() const;
virtual int bitrate() const;
virtual int sampleRate() const;
virtual int channels() const;
int formatVersion() const;
int formatID() const;
/*!
* Channel type values: 1 = mono, 2 = stereo, 3 = 3 channels,
* 4 = quad, 5 = 4 channels, 6 = 5 channels, 7 = 5.1 channels
*/
int channelType() const;
int bitsPerSample() const;
long long sampleCount() const;
int blockSizePerChannel() const;
private:
Properties(const Properties &);
Properties &operator=(const Properties &);
void read(const ByteVector &data);
class PropertiesPrivate;
PropertiesPrivate *d;
};
}
}
#endif

View File

@@ -52,6 +52,8 @@
#include "s3mfile.h"
#include "itfile.h"
#include "xmfile.h"
#include "dsffile.h"
#include "dsdifffile.h"
using namespace TagLib;
@@ -135,6 +137,10 @@ namespace
return new IT::File(stream, readAudioProperties, audioPropertiesStyle);
if(ext == "XM")
return new XM::File(stream, readAudioProperties, audioPropertiesStyle);
if(ext == "DFF" || ext == "DSDIFF")
return new DSDIFF::File(stream, readAudioProperties, audioPropertiesStyle);
if(ext == "DSF")
return new DSF::File(stream, readAudioProperties, audioPropertiesStyle);
return 0;
}
@@ -174,6 +180,10 @@ namespace
file = new RIFF::WAV::File(stream, readAudioProperties, audioPropertiesStyle);
else if(APE::File::isSupported(stream))
file = new APE::File(stream, readAudioProperties, audioPropertiesStyle);
else if(DSDIFF::File::isSupported(stream))
file = new DSDIFF::File(stream, readAudioProperties, audioPropertiesStyle);
else if(DSF::File::isSupported(stream))
file = new DSF::File(stream, readAudioProperties, audioPropertiesStyle);
// isSupported() only does a quick check, so double check the file here.
@@ -255,6 +265,10 @@ namespace
return new IT::File(fileName, readAudioProperties, audioPropertiesStyle);
if(ext == "XM")
return new XM::File(fileName, readAudioProperties, audioPropertiesStyle);
if(ext == "DFF" || ext == "DSDIFF")
return new DSDIFF::File(fileName, readAudioProperties, audioPropertiesStyle);
if(ext == "DSF")
return new DSF::File(fileName, readAudioProperties, audioPropertiesStyle);
return 0;
}
@@ -387,6 +401,9 @@ StringList FileRef::defaultFileExtensions()
l.append("s3m");
l.append("it");
l.append("xm");
l.append("dsf");
l.append("dff");
l.append("dsdiff"); // alias for "dff"
return l;
}

View File

@@ -216,7 +216,23 @@ void TableOfContentsFrame::removeEmbeddedFrames(const ByteVector &id)
String TableOfContentsFrame::toString() const
{
return String();
String s = String(d->elementID) +
": top level: " + (d->isTopLevel ? "true" : "false") +
", ordered: " + (d->isOrdered ? "true" : "false");
if(!d->childElements.isEmpty()) {
s+= ", chapters: [ " + String(d->childElements.toByteVector(", ")) + " ]";
}
if(!d->embeddedFrameList.isEmpty()) {
StringList frameIDs;
for(FrameList::ConstIterator it = d->embeddedFrameList.begin();
it != d->embeddedFrameList.end(); ++it)
frameIDs.append((*it)->frameID());
s += ", sub-frames: [ " + frameIDs.toString(", ") + " ]";
}
return s;
}
PropertyMap TableOfContentsFrame::asProperties() const

View File

@@ -339,7 +339,13 @@ UserTextIdentificationFrame::UserTextIdentificationFrame(const String &descripti
String UserTextIdentificationFrame::toString() const
{
return "[" + description() + "] " + fieldList().toString();
// first entry is the description itself, drop from values list
StringList l = fieldList();
for(StringList::Iterator it = l.begin(); it != l.end(); ++it) {
l.erase(it);
break;
}
return "[" + description() + "] " + l.toString();
}
String UserTextIdentificationFrame::description() const

View File

@@ -231,11 +231,21 @@ void Ogg::FLAC::File::scan()
if(!metadataHeader.startsWith("fLaC")) {
// FLAC 1.1.2+
// See https://xiph.org/flac/ogg_mapping.html for the header specification.
if(metadataHeader.size() < 13)
return;
if(metadataHeader[0] != 0x7f)
return;
if(metadataHeader.mid(1, 4) != "FLAC")
return;
if(metadataHeader[5] != 1)
return; // not version 1
if(metadataHeader[5] != 1 && metadataHeader[6] != 0)
return; // not version 1.0
if(metadataHeader.mid(9, 4) != "fLaC")
return;
metadataHeader = metadataHeader.mid(13);
}

View File

@@ -63,6 +63,8 @@
#include "itfile.h"
#include "xmfile.h"
#include "mp4file.h"
#include "dsffile.h"
#include "dsdifffile.h"
using namespace TagLib;
@@ -148,6 +150,10 @@ PropertyMap File::properties() const
return dynamic_cast<const MP4::File* >(this)->properties();
if(dynamic_cast<const ASF::File* >(this))
return dynamic_cast<const ASF::File* >(this)->properties();
if(dynamic_cast<const DSF::File* >(this))
return dynamic_cast<const DSF::File* >(this)->properties();
if(dynamic_cast<const DSDIFF::File* >(this))
return dynamic_cast<const DSDIFF::File* >(this)->properties();
return tag()->properties();
}
@@ -177,6 +183,10 @@ void File::removeUnsupportedProperties(const StringList &properties)
dynamic_cast<MP4::File* >(this)->removeUnsupportedProperties(properties);
else if(dynamic_cast<ASF::File* >(this))
dynamic_cast<ASF::File* >(this)->removeUnsupportedProperties(properties);
else if(dynamic_cast<DSF::File* >(this))
dynamic_cast<DSF::File* >(this)->removeUnsupportedProperties(properties);
else if(dynamic_cast<DSDIFF::File* >(this))
dynamic_cast<DSDIFF::File* >(this)->removeUnsupportedProperties(properties);
else
tag()->removeUnsupportedProperties(properties);
}
@@ -219,6 +229,10 @@ PropertyMap File::setProperties(const PropertyMap &properties)
return dynamic_cast<MP4::File* >(this)->setProperties(properties);
else if(dynamic_cast<ASF::File* >(this))
return dynamic_cast<ASF::File* >(this)->setProperties(properties);
else if(dynamic_cast<DSF::File* >(this))
return dynamic_cast<DSF::File* >(this)->setProperties(properties);
else if(dynamic_cast<DSDIFF::File* >(this))
return dynamic_cast<DSDIFF::File* >(this)->setProperties(properties);
else
return tag()->setProperties(properties);
}

View File

@@ -58,6 +58,11 @@ namespace
#endif
}
FileHandle openFile(const int fileDescriptor, bool readOnly)
{
return InvalidFileHandle;
}
void closeFile(FileHandle file)
{
CloseHandle(file);
@@ -98,6 +103,11 @@ namespace
return fopen(path, readOnly ? "rb" : "rb+");
}
FileHandle openFile(const int fileDescriptor, bool readOnly)
{
return fdopen(fileDescriptor, readOnly ? "rb" : "rb+");
}
void closeFile(FileHandle file)
{
fclose(file);
@@ -149,13 +159,28 @@ FileStream::FileStream(FileName fileName, bool openReadOnly)
d->file = openFile(fileName, true);
if(d->file == InvalidFileHandle)
{
# ifdef _WIN32
debug("Could not open file " + fileName.toString());
# else
debug("Could not open file " + String(static_cast<const char *>(d->name)));
# endif
}
}
FileStream::FileStream(int fileDescriptor, bool openReadOnly)
: d(new FileStreamPrivate(""))
{
// First try with read / write mode, if that fails, fall back to read only.
if(!openReadOnly)
d->file = openFile(fileDescriptor, false);
if(d->file != InvalidFileHandle)
d->readOnly = false;
else
d->file = openFile(fileDescriptor, true);
if(d->file == InvalidFileHandle)
debug("Could not open file using file descriptor");
}
FileStream::~FileStream()
@@ -255,8 +280,7 @@ void FileStream::insert(const ByteVector &data, unsigned long start, unsigned lo
ByteVector buffer = data;
ByteVector aboutToOverwrite(static_cast<unsigned int>(bufferLength));
while(true)
{
while(true) {
// Seek to the current read position and read the data that we're about
// to overwrite. Appropriately increment the readPosition.
@@ -304,8 +328,7 @@ void FileStream::removeBlock(unsigned long start, unsigned long length)
ByteVector buffer(static_cast<unsigned int>(bufferLength));
for(unsigned int bytesRead = -1; bytesRead != 0;)
{
for(unsigned int bytesRead = -1; bytesRead != 0;) {
seek(readPosition);
bytesRead = static_cast<unsigned int>(readFile(d->file, buffer));
readPosition += bytesRead;
@@ -401,7 +424,8 @@ long FileStream::tell() const
const LARGE_INTEGER zero = {};
LARGE_INTEGER position;
if(SetFilePointerEx(d->file, zero, &position, FILE_CURRENT) && position.QuadPart <= LONG_MAX) {
if(SetFilePointerEx(d->file, zero, &position, FILE_CURRENT) &&
position.QuadPart <= LONG_MAX) {
return static_cast<long>(position.QuadPart);
}
else {
@@ -470,9 +494,8 @@ void FileStream::truncate(long length)
#else
const int error = ftruncate(fileno(d->file), length);
if(error != 0) {
if(error != 0)
debug("FileStream::truncate() -- Coundn't truncate the file.");
}
#endif
}

View File

@@ -54,6 +54,11 @@ namespace TagLib {
*/
FileStream(FileName file, bool openReadOnly = false);
/*!
* Construct a File object and opens the \a file using file descriptor.
*/
FileStream(int fileDescriptor, bool openReadOnly = false);
/*!
* Destroys this FileStream instance.
*/

View File

@@ -1,7 +1,5 @@
# Strawberry Music Player
# Copyright 2013, Jonas Kvinge <jonas@strawbs.net>
# This file was part of Clementine.
# Copyright 2010, David Sansome <me@davidsansome.com>
#
# Strawberry is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
@@ -18,7 +16,9 @@
project(strawberry)
cmake_minimum_required(VERSION 2.8.11)
cmake_policy(SET CMP0054 NEW)
if(CMAKE_VERSION VERSION_GREATER 3.0)
cmake_policy(SET CMP0054 NEW)
endif()
include(CheckCXXCompilerFlag)
include(CheckIncludeFiles)
@@ -29,26 +29,31 @@ include(cmake/Summary.cmake)
include(cmake/OptionalSource.cmake)
include(cmake/ParseArguments.cmake)
include(cmake/Rpm.cmake)
include(cmake/Deb.cmake)
include(cmake/Dmg.cmake)
if (${CMAKE_SYSTEM_NAME} STREQUAL "Linux")
set(LINUX 1)
set(LINUX ON)
endif()
if (${CMAKE_SYSTEM_NAME} STREQUAL "FreeBSD")
set(FREEBSD 1)
set(FREEBSD ON)
endif()
if (${CMAKE_SYSTEM_NAME} STREQUAL "OpenBSD")
set(OPENBSD 1)
set(OPENBSD ON)
endif()
set(CMAKE_CXX_STANDARD 11)
set(CMAKE_MODULE_PATH ${CMAKE_SOURCE_DIR}/cmake)
#set(CMAKE_BUILD_TYPE Debug)
if(${CMAKE_BUILD_TYPE} MATCHES "Release")
add_definitions(-DNDEBUG)
add_definitions(-DQT_NO_DEBUG_OUTPUT)
#add_definitions(-DQT_NO_WARNING_OUTPUT)
endif(${CMAKE_BUILD_TYPE} MATCHES "Release")
endif()
if(${CMAKE_BUILD_TYPE} MATCHES "Debug")
set(DEBUG ON)
endif()
if (CMAKE_CXX_COMPILER MATCHES ".*clang")
set(CMAKE_COMPILER_IS_CLANGXX 1)
@@ -79,43 +84,44 @@ find_package(Protobuf REQUIRED)
find_library(PROTOBUF_STATIC_LIBRARY libprotobuf.a libprotobuf)
if(LINUX)
find_package(ALSA REQUIRED)
find_package(DBus REQUIRED)
pkg_check_modules(DBUS REQUIRED dbus-1)
else(LINUX)
find_package(ALSA)
find_package(DBus)
pkg_check_modules(DBUS dbus-1)
endif(LINUX)
if(ALSA_FOUND)
set(HAVE_ALSA ON)
if (UNIX AND NOT APPLE)
find_package(X11)
pkg_check_modules(XCB xcb)
endif()
if(X11_FOUND)
set(HAVE_X11 ON)
endif()
find_package(X11)
pkg_check_modules(GSTREAMER gstreamer-1.0)
pkg_check_modules(GSTREAMER_BASE gstreamer-base-1.0)
pkg_check_modules(GSTREAMER_AUDIO gstreamer-audio-1.0)
pkg_check_modules(GSTREAMER_APP gstreamer-app-1.0)
pkg_check_modules(GSTREAMER_TAG gstreamer-tag-1.0)
pkg_check_modules(GSTREAMER_PBUTILS gstreamer-pbutils-1.0)
pkg_check_modules(LIBXINE libxine)
pkg_check_modules(LIBVLC libvlc)
pkg_check_modules(PHONON phonon4qt5)
pkg_check_modules(SQLITE REQUIRED sqlite3>=3.7)
find_package(OpenGL REQUIRED)
pkg_check_modules(CHROMAPRINT REQUIRED libchromaprint)
pkg_check_modules(LIBPULSE libpulse)
pkg_check_modules(CHROMAPRINT libchromaprint)
pkg_check_modules(LIBGPOD libgpod-1.0>=0.7.92)
pkg_check_modules(LIBMTP libmtp>=1.0)
pkg_check_modules(LIBPULSE libpulse)
pkg_check_modules(LIBXML libxml-2.0)
pkg_check_modules(LIBGLU REQUIRED glu)
pkg_check_modules(IMOBILEDEVICE libimobiledevice-1.0)
pkg_check_modules(USBMUXD libusbmuxd)
pkg_check_modules(PLIST libplist)
pkg_check_modules(LIBIMOBILEDEVICE libimobiledevice-1.0)
pkg_check_modules(LIBUSBMUXD libusbmuxd)
pkg_check_modules(LIBPLIST libplist)
pkg_check_modules(LIBDEEZER libdeezer)
pkg_check_modules(LIBDZMEDIA libdzmedia)
if(WIN32)
find_package(ZLIB REQUIRED)
endif(WIN32)
# QT
set(QT_MIN_VERSION 5.6.0)
find_package(Qt5 ${QT_MIN_VERSION} REQUIRED COMPONENTS Core OpenGL Sql Network Xml Widgets Concurrent Test)
set(QT_MIN_VERSION 5.5)
find_package(Qt5 ${QT_MIN_VERSION} REQUIRED COMPONENTS Core Concurrent Widgets Network Sql)
if(X11_FOUND)
find_package(Qt5 ${QT_MIN_VERSION} REQUIRED COMPONENTS X11Extras)
endif()
@@ -123,43 +129,68 @@ if(DBUS_FOUND)
find_package(Qt5 ${QT_MIN_VERSION} REQUIRED COMPONENTS DBus)
get_target_property(QT_DBUSXML2CPP_EXECUTABLE Qt5::qdbusxml2cpp LOCATION)
endif()
if(NOT APPLE)
find_package(Qt5 COMPONENTS WebKitWidgets)
endif(NOT APPLE)
if(APPLE)
if(NOT QT_MAC_USE_COCOA)
message(FATAL_ERROR "Cocoa support is required")
endif(NOT QT_MAC_USE_COCOA)
endif(APPLE)
if(UNIX AND X11_FOUND AND DBUS_FOUND)
set(QT_LIBRARIES Qt5::Core Qt5::Concurrent Qt5::Widgets Qt5::Network Qt5::Sql Qt5::OpenGL Qt5::Xml Qt5::X11Extras Qt5::DBus)
elseif(UNIX AND X11_FOUND)
set(QT_LIBRARIES Qt5::Core Qt5::Concurrent Qt5::Widgets Qt5::Network Qt5::Sql Qt5::OpenGL Qt5::Xml Qt5::X11Extras)
else()
set(QT_LIBRARIES Qt5::Core Qt5::Concurrent Qt5::Widgets Qt5::Network Qt5::Sql Qt5::OpenGL Qt5::Xml)
find_package(Qt5 REQUIRED COMPONENTS MacExtras)
endif()
if(WIN32)
find_package(Qt5 REQUIRED COMPONENTS WinExtras)
endif()
# Don't try to use webkit if their include directories couldn't be found.
if (NOT QT_QTWEBKIT_INCLUDE_DIR)
set (QT_USE_QTWEBKIT 0)
endif (NOT QT_QTWEBKIT_INCLUDE_DIR)
set(QT_LIBRARIES Qt5::Core Qt5::Concurrent Qt5::Widgets Qt5::Network Qt5::Sql)
if(DBUS_FOUND)
set(QT_LIBRARIES ${QT_LIBRARIES} Qt5::DBus)
endif()
if(X11_FOUND)
set(QT_LIBRARIES ${QT_LIBRARIES} Qt5::X11Extras)
endif()
if(APPLE)
set(QT_LIBRARIES ${QT_LIBRARIES} Qt5::MacExtras)
endif()
if(WIN32)
set(QT_LIBRARIES ${QT_LIBRARIES} Qt5::WinExtras)
endif()
if(X11_FOUND)
find_path(KEYSYMDEF_H NAMES "keysymdef.h" PATHS "${X11_INCLUDE_DIR}" PATH_SUFFIXES "X11")
find_path(XF86KEYSYM_H NAMES "XF86keysym.h" PATHS "${XCB_INCLUDEDIR}" PATH_SUFFIXES "X11")
if(KEYSYMDEF_H)
set(HAVE_KEYSYMDEF_H ON)
else()
message(WARNING, "Missing X11/keysymdef.h")
endif()
if(XF86KEYSYM_H)
set(HAVE_XF86KEYSYM_H ON)
else()
message(WARNING, "Missing X11/XF86keysym.h")
endif()
endif(X11_FOUND)
# TAGLIB
pkg_check_modules(TAGLIB taglib)
# Only use system taglib if it's greater than 1.11.1 because of audio file detection by content.
# But let the user override as strawberry will still compile and work without.
# Only use system taglib if it's greater than 1.11.1
# There is a bug in 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
# The current taglib in 3rdparty also has the following features used by strawberry:
# - Audio file detection by content.
# - DSF and DSDIFF support
#
if (TAGLIB_VERSION VERSION_GREATER 1.11.1 OR WIN32)
option(USE_SYSTEM_TAGLIB "Use system taglib" ON)
else()
option(USE_SYSTEM_TAGLIB "Use system taglib" OFF)
endif()
if (TAGLIB_FOUND AND USE_SYSTEM_TAGLIB)
message(STATUS "Using system taglib library")
set(CMAKE_REQUIRED_INCLUDES "${TAGLIB_INCLUDE_DIRS}")
set(CMAKE_REQUIRED_LIBRARIES "${TAGLIB_LIBRARIES}")
set(CMAKE_REQUIRED_INCLUDES)
set(CMAKE_REQUIRED_LIBRARIES)
if (TAGLIB_VERSION VERSION_GREATER 1.11.1 OR WIN32)
message(STATUS "Using system taglib library")
else()
message(WARNING "Using system taglib library. Version 1.11.1 or less has a bug corrupting Ogg files, make sure your systems version has been patched!")
endif()
find_path(HAVE_TAGLIB_DSFFILE_H taglib/dsffile.h)
if(HAVE_TAGLIB_DSFFILE_H)
set(HAVE_TAGLIB_DSFFILE ON)
endif(HAVE_TAGLIB_DSFFILE_H)
else()
message(STATUS "Using builtin taglib library")
set(TAGLIB_INCLUDE_DIRS "${CMAKE_BINARY_DIR}/3rdparty/taglib/headers/taglib/;${CMAKE_BINARY_DIR}/3rdparty/taglib/headers/")
@@ -167,75 +198,42 @@ else()
set(TAGLIB_LIBRARIES tag)
add_subdirectory(3rdparty/utf8-cpp)
add_subdirectory(3rdparty/taglib)
set(HAVE_TAGLIB_DSFFILE ON)
endif()
# LASTFM
find_library(LASTFM5_LIBRARIES lastfm5)
find_path(LASTFM5_INCLUDE_DIRS lastfm5/ws.h)
find_path(LASTFM51_INCLUDE_DIRS lastfm5/Track.h)
if(LASTFM5_INCLUDE_DIRS AND LASTFM51_INCLUDE_DIRS)
set(HAVE_LIBLASTFM1 ON)
# SingleApplication
pkg_check_modules(SINGLEAPPLICATION singleapplication)
if (NOT SINGLEAPPLICATION_FOUND)
pkg_check_modules(SINGLEAPPLICATION libsingleapplication)
endif()
# CHROMAPRINT
CHECK_INCLUDE_FILES(chromaprint.h CHROMAPRINT_H)
# Use system sha2 if it's available
find_path(SHA2_INCLUDE_DIRS sha2.h)
find_library(SHA2_LIBRARIES sha2)
if (SHA2_LIBRARIES AND SHA2_INCLUDE_DIRS)
message(STATUS "Using system sha2 library")
set(USE_SYSTEM_SHA2 ON)
else()
message(STATUS "Using builtin sha2 library")
set(USE_SYSTEM_SHA2 OFF)
add_subdirectory(3rdparty/sha2)
set(SHA2_INCLUDE_DIRS ${CMAKE_CURRENT_SOURCE_DIR}/3rdparty/sha2)
set(SHA2_LIBRARIES sha2)
pkg_check_modules(SINGLECOREAPPLICATION singlecoreapplication)
if (NOT SINGLECOREAPPLICATION_FOUND)
pkg_check_modules(SINGLECOREAPPLICATION libsinglecoreapplication)
endif()
# Use system QtSingleApplication only if explicitly enabled.
option(USE_SYSTEM_QTSINGLEAPPLICATION "Use system QtSingleApplication library" OFF)
if(USE_SYSTEM_QTSINGLEAPPLICATION)
message(STATUS "Using system QtSingleApplication library")
find_path(QTSINGLEAPPLICATION_INCLUDE_DIRS qtsingleapplication.h PATH_SUFFIXES QtSolutions)
find_library(QTSINGLEAPPLICATION_LIBRARIES QtSolutions_SingleApplication-2.6)
find_library(QTSINGLECOREAPPLICATION_LIBRARIES QtSolutions_SingleCoreApplication-2.6)
else(USE_SYSTEM_QTSINGLEAPPLICATION)
message(STATUS "Using builtin QtSingleApplication library")
add_subdirectory(3rdparty/qtsingleapplication)
set(QTSINGLEAPPLICATION_INCLUDE_DIRS ${CMAKE_CURRENT_SOURCE_DIR}/3rdparty/qtsingleapplication)
set(QTSINGLEAPPLICATION_LIBRARIES qtsingleapplication)
endif(USE_SYSTEM_QTSINGLEAPPLICATION)
# Use system Qxt only if explicitly enabled.
option(USE_SYSTEM_QXT "Use system Qxt library" OFF)
if (USE_SYSTEM_QXT)
message(STATUS "Using system Qxt library")
find_path(QXTCORE_INCLUDE_DIRS qxtglobal.h PATH_SUFFIXES QxtCore)
find_path(QXTGUI_INCLUDE_DIRS qxtglobalshortcut.h PATH_SUFFIXES QxtGui)
set(QXT_INCLUDE_DIRS ${QXTCORE_INCLUDE_DIRS} ${QXTGUI_INCLUDE_DIRS})
# We only need its header. We don't need to link to QxtCore.
find_library(QXT_LIBRARIES QxtGui)
else (USE_SYSTEM_QXT)
message(STATUS "Using builtin Qxt library")
add_definitions(-DQXT_STATIC -DBUILD_QXT_GUI -DBUILD_QXT_CORE)
set(QXT_INCLUDE_DIRS ${CMAKE_CURRENT_SOURCE_DIR}/3rdparty/qxt)
set(QXT_LIBRARIES qxt)
if (NOT APPLE)
add_subdirectory(3rdparty/qxt)
endif (NOT APPLE)
endif (USE_SYSTEM_QXT)
if (SINGLEAPPLICATION_FOUND AND SINGLECOREAPPLICATION_FOUND)
option(USE_SYSTEM_SINGLEAPPLICATION "Use system SingleApplication/SingleCoreApplication libraries" ON)
else(SINGLEAPPLICATION_FOUND AND SINGLECOREAPPLICATION_FOUND)
option(USE_SYSTEM_SINGLEAPPLICATION "Use system SingleApplication/SingleCoreApplication libraries" OFF)
endif(SINGLEAPPLICATION_FOUND AND SINGLECOREAPPLICATION_FOUND)
if(USE_SYSTEM_SINGLEAPPLICATION)
if (NOT SINGLEAPPLICATION_FOUND OR NOT SINGLECOREAPPLICATION_FOUND)
message(FATAL_ERROR "Missing SingleApplication or SingleCoreApplication")
endif()
message(STATUS "Using system SingleApplication and SingleCoreApplication libraries")
else(USE_SYSTEM_SINGLEAPPLICATION)
message(STATUS "Using builtin SingleApplication and SingleCoreApplication libraries")
add_subdirectory(3rdparty/singleapplication)
set(SINGLEAPPLICATION_INCLUDE_DIRS ${CMAKE_CURRENT_SOURCE_DIR}/3rdparty/singleapplication)
set(SINGLEAPPLICATION_LIBRARIES singleapplication)
set(SINGLECOREAPPLICATION_LIBRARIES singlecoreapplication)
unset(SINGLEAPPLICATION_LIBRARY_DIRS)
unset(SINGLECOREAPPLICATION_LIBRARY_DIRS)
endif(USE_SYSTEM_SINGLEAPPLICATION)
# Qocoa
set(QOCOA_LIBRARIES Qocoa)
add_subdirectory(3rdparty/qocoa)
# Windows
if (WIN32)
add_subdirectory(3rdparty/qtwin)
endif (WIN32)
if (APPLE)
find_library(SPARKLE Sparkle)
add_subdirectory(3rdparty/SPMediaKeyTap)
@@ -255,6 +253,14 @@ if(WIN32)
option(ENABLE_WIN32_CONSOLE "Show the windows console even outside Debug mode" OFF)
endif(WIN32)
optional_component(ALSA ON "ALSA integration"
DEPENDS "alsa" ALSA_FOUND
)
optional_component(LIBPULSE ON "Pulse audio integration"
DEPENDS "libpulse" LIBPULSE_FOUND
)
optional_component(DBUS ON "D-Bus support"
DEPENDS "D-Bus" DBUS_FOUND
)
@@ -265,49 +271,48 @@ optional_component(GSTREAMER ON "Engine: GStreamer backend"
DEPENDS "gstreamer-app-1.0" GSTREAMER_APP_FOUND
DEPENDS "gstreamer-audio-1.0" GSTREAMER_AUDIO_FOUND
DEPENDS "gstreamer-tag-1.0" GSTREAMER_TAG_FOUND
DEPENDS "gstreamer-pbutils-1.0" GSTREAMER_PBUTILS_FOUND
)
optional_component(XINE OFF "Engine: Xine backend"
optional_component(XINE ON "Engine: Xine backend"
DEPENDS "libxine" LIBXINE_FOUND
)
optional_component(VLC OFF "Engine: VLC backend"
optional_component(VLC ON "Engine: VLC backend"
DEPENDS "libvlc" LIBVLC_FOUND
)
optional_component(PHONON OFF "Engine: Phonon backend"
optional_component(PHONON OFF "Engine: Phonon backend (UNSTABLE)"
DEPENDS "phonon4qt5" PHONON_FOUND
)
if (WIN32)
optional_component(DEEZER ON "Engine: Deezer backend"
DEPENDS "libdeezer" LIBDEEZER_FOUND
)
else ()
optional_component(DEEZER ON "Engine: Deezer backend"
DEPENDS "Linux" LINUX
DEPENDS "libdeezer" LIBDEEZER_FOUND
DEPENDS "libpulse" LIBPULSE_FOUND
)
endif()
optional_component(CHROMAPRINT ON "Chromaprint (Tag fetching from Musicbrainz)"
DEPENDS "chromaprint" CHROMAPRINT_FOUND
)
if (X11_FOUND OR HAVE_DBUS OR APPLE OR WIN32)
set(HAVE_GLOBALSHORTCUTS_SUPPORT ON)
endif()
optional_component(GLOBALSHORTCUTS ON "Global shortcuts"
DEPENDS "D-Bus, X11, Windows or macOS" HAVE_GLOBALSHORTCUTS_SUPPORT
)
optional_component(AUDIOCD ON "Devices: Audio CD support"
DEPENDS "libcdio" CDIO_FOUND
)
optional_component(LIBGPOD ON "Devices: iPod classic support"
DEPENDS "libgpod" LIBGPOD_FOUND
)
optional_component(GIO ON "Devices: GIO device backend"
DEPENDS "libgio" GIO_FOUND
DEPENDS "Unix or Windows" "NOT APPLE"
)
optional_component(IMOBILEDEVICE ON "Devices: iPod Touch, iPhone, iPad support"
DEPENDS "libimobiledevice" IMOBILEDEVICE_FOUND
DEPENDS "libplist" PLIST_FOUND
DEPENDS "libusbmuxd" USBMUXD_FOUND
DEPENDS "iPod classic support" HAVE_LIBGPOD
)
optional_component(LIBMTP ON "Devices: MTP support"
DEPENDS "libmtp" LIBMTP_FOUND
)
optional_component(LIBLASTFM ON "Last.fm album cover provider"
DEPENDS "liblastfm" LASTFM5_LIBRARIES LASTFM5_INCLUDE_DIRS
)
optional_component(DEVICEKIT ON "Devices: DeviceKit backend"
DEPENDS "D-Bus support" DBUS_FOUND
)
@@ -316,18 +321,57 @@ optional_component(UDISKS2 ON "Devices: UDisks2 backend"
DEPENDS "D-Bus support" DBUS_FOUND
)
optional_component(LIBPULSE ON "Pulse audio integration"
DEPENDS "libpulse" LIBPULSE_FOUND
optional_component(GIO ON "Devices: GIO device backend"
DEPENDS "libgio" GIO_FOUND
DEPENDS "Unix or Windows" "NOT APPLE"
)
optional_component(LIBGPOD ON "Devices: iPod classic support"
DEPENDS "libgpod" LIBGPOD_FOUND
)
optional_component(LIBMTP ON "Devices: MTP support"
DEPENDS "libmtp" LIBMTP_FOUND
)
optional_component(IMOBILEDEVICE ON "Devices: iPhone, iPod Touch, iPad and Apple TV support"
DEPENDS "libimobiledevice" LIBIMOBILEDEVICE_FOUND
DEPENDS "libplist" LIBPLIST_FOUND
DEPENDS "libusbmuxd" LIBUSBMUXD_FOUND
DEPENDS "libgpod" LIBGPOD_FOUND
)
optional_component(SPARKLE ON "Sparkle integration"
DEPENDS "Mac OS X" APPLE
DEPENDS "macOS" APPLE
DEPENDS "Sparkle" SPARKLE
)
#if(IMOBILEDEVICE_FOUND AND PLIST_FOUND)
#add_subdirectory(ext/gstafc)
#endif(IMOBILEDEVICE_FOUND AND PLIST_FOUND)
optional_component(STREAM_TIDAL ON "Streaming: Tidal support")
if (LIBDZMEDIA_FOUND OR LIBDEEZER_FOUND)
optional_component(STREAM_DEEZER ON "Streaming: Deezer support")
else()
optional_component(STREAM_DEEZER OFF "Streaming: Deezer support")
endif()
optional_component(DZMEDIA ON "DZMedia"
DEPENDS "libdzmedia" LIBDZMEDIA_FOUND
DEPENDS "Deezer support" HAVE_STREAM_DEEZER
)
if (HAVE_STREAM_DEEZER AND NOT HAVE_DZMEDIA AND NOT HAVE_DEEZER)
message(STATUS "Deezer is enabled, but not DZMedia or Deezer engine, only preview streams will be available.")
endif()
if(APPLE)
option(USE_BUNDLE "Bundle macOS dependencies" OFF)
elseif(WIN32)
option(USE_BUNDLE "Bundle Windows dependencies" ON)
endif()
if (APPLE AND USE_BUNDLE AND NOT USE_BUNDLE_DIR)
set(USE_BUNDLE_DIR "../PlugIns")
endif()
# Set up definitions and paths
@@ -339,8 +383,9 @@ add_definitions(-DQT_NO_CAST_TO_ASCII -DQT_STRICT_ITERATORS)
include_directories(${GLIB_INCLUDE_DIRS})
include_directories(${GLIBCONFIG_INCLUDE_DIRS})
include_directories(${TAGLIB_INCLUDE_DIRS})
if(ENABLE_IMOBILEDEVICE AND IMOBILEDEVICE_VERSION VERSION_GREATER 1.1.1)
if(HAVE_IMOBILEDEVICE AND LIBIMOBILEDEVICE_VERSION VERSION_GREATER 1.1.1)
set(IMOBILEDEVICE_USES_UDIDS ON)
endif()
@@ -362,6 +407,8 @@ add_custom_target(uninstall
# Show a summary of what we have enabled
summary_show()
if(NOT HAVE_GSTREAMER AND NOT HAVE_XINE AND NOT HAVE_VLC AND NOT HAVE_PHONON)
message(FATAL_ERROR "You need to enable either GStreamer, Xine, VLC or Phonon to compile!")
if(NOT HAVE_GSTREAMER AND NOT HAVE_XINE AND NOT HAVE_VLC AND NOT HAVE_PHONON AND NOT HAVE_DEEZER)
message(FATAL_ERROR "You need to have either GStreamer, Xine, VLC, Phonon or Deezer to compile!")
elseif(NOT HAVE_GSTREAMER)
message(WARNING "GStreamer is the only engine that is fully implemented. Using other engines is possible but not recommended.")
endif()

134
Changelog
View File

@@ -2,9 +2,137 @@ Strawberry Music Player
=======================
ChangeLog
Version 0.5.2:
* Added error handling and message for URL handler
* Added SingleCoreApplication secondary check
* Fixed memory leaks in devices
* Fixed more stability issues in devices
* Remove remastered from album title when searching for lyrics, covers and scrobbling
* Fixed CMake check for X11 headers
* Enabled iLister and AFC device
* Added saving of album cover to gpod devices
* Fixed AAC/MP4 transcoder
* Added log to organise error dialog
* Added warning when enabling X11 shortcuts on Gnome, Cinnamon or KDE
* Fixed transcoder to use filename suffixes to solve issues when copying files to iPods
* Added option to copy album cover in organise dialog (filesystem and libgpod devices)
* Added raise() to make sure window is on top when strawberry is started twice
Version 0.5.1:
* Added scrobbler with support for Last.fm, Libre.fm and ListenBrainz
* Fixed key up causing playback to reset
* New improved last.fm and musicbrainz album cover providers
* Removed Qt5Xml and liblastfm dependency
* Replaced sha2 with QCryptographicHash
* Fixed songs not showing up under devices
* Removed qxt dependency
* Replaced globalshortcuts backend for X11 and Windows
* Made global shortcuts on X11 optional and off by default
* Added support for Monkey's Audio
* Fixed crashes in devices
* Enabled transcoder and organiser in menus
* Added transcoder for WavPack
* Changed default group by for collection to album artist
* Enabled organise without gstreamer
* Added cmake USE_BUNDLE option for macOS/Windows
* Fixed broken GSD D-Bus shortcuts backend
* Fixed broken background image setting
* Disabling fading when a ALSA device is selected
* Replaced QtSingleApplication with SingleApplication
* Fixed scroll over icon to change track feature
* Fixed freeze when changing appearance settings
* Made text in about dialog selectable
* Improved manual pages
* Fixed typos
* Updated contributors in about
* Added debian copyright file
* Fixed some compile errors
Version 0.4.2:
* Updated AppStream data file to newer specifications
* Fixed Deezer engine to use quality setting
* Removed unneeded dependency Qt5OpenGL
* Removed obsolete xine warning and engine reinitialization
* Added ALSA as optional component in cmake
* Fixed bug in playlist columns setting all visible
* Added option to reset playlist columns
* Fixed/Improved console logging
* Added queue to play next option
* (Windows) Corrected uninstalled files on x64 installer
* (macOS) Fixed poor performance
Version 0.4.1:
* Fixed crash in analyzer
* Fixed trying to use systray even if the desktop had no systray
* Fixed Tidal login loop bug
* Added Deezer support
* New improved fancy tabwidget
* Fixed bug not loading engine settings
* Moved queue manager into tabbar for easier access
* Fixed crash when changing appearance colors
* Improved error handling between player and engine
* Added setting to allow continue to the next song in the playlist based on error
* Improved Xine engine code
* Moved some settings
* Updated builtin taglib
* Added AppStream data file
* Fixed compiling with Qt 5 versions of system QtSingleApplication and Qxt library
Version 0.3.3:
* Fixed Tidal login
Version 0.3.2:
* Fixed search error not shown in Tidal search
* Added URL handler for Tidal, now retrieving URL's when playing instead of when searching
* Fixed bug in pipeline not setting url
* Fixed bug setting wrong temporary metadata
* Removed device module from windows, since it's not implemented for windows
* Added support for both ALSA hw and plughw
* Added option to change url stream scheme for Tidal
* Added encoding of Tidal token in the source code
* Added encoding of Tidal password in the configuration
Version 0.3.1:
* Added new lyrics provider with lyrics from AudD and API Seeds
* New improved context widget with albums and lyrics
* Fixed playing and context widget getting stuck in play mode when there was an error
* Changed icons for artists in collection, tidal and cover manager
* Removed "search" icon from "Search automatically" checkbox (right click) that looked ugly
* Removed some unused widgets from the src/widgets directory
* Fixed initial size of window and side panel
* Fixed saving window size correctly
* Added Tidal support
* Disabled Amazon cover provider because of revoked API key
* Removed broken xine fader
* Made chromeprint optional
* Added missing names to about dialog
* Made xine enabled only for window debug
* Removed dead code
* Added DSF and DSDIFF/DFF support
* Fixed tagreader crash when saving tags to MP3 files
* Added support for reading/writing lyrics to tags
* Fixed saving tags (APE) for WavPack files
Version 0.2.1:
* Fixed crash with newer Qt
* Fixed setting output/device for Xine and VLC backend
* Improved backend settings to better support multiple backends
* Fixed track slider for Xine, VLC and Phonon
* Fixed compilation on macOS
* Fixed device selection on macOS
* Added xine on to windows build
Version 0.1.6:
* Fix crash on exit caused by NVIDIA driver
* Fix PulseAudio device selection
* Fixed crash on exit caused by NVIDIA driver
* Fixed PulseAudio device selection
* Improvements to device selection
Version 0.1.5:
@@ -17,7 +145,7 @@ Version 0.1.4:
Version 0.1.3:
* Audio file detection by content
* Added builtin taglib to 3rdparty to support detecting audio by content instead of just file extension
* Removed unneeded bulitin qsqlite from 3rdparty
* Removed unneeded qsqlite from 3rdparty
* Added sqldrivers\qsqlite.dll for windows build
* Replaced incorrect DLL libgstdirectsoundsink.dll (from gst 1.12.4) instead of libgstdirectsound.dll (from gst 1.14.0) for windows build
* Fixed git versioning

17
Dockerfile Normal file
View File

@@ -0,0 +1,17 @@
from opensuse:tumbleweed
run zypper --non-interactive --gpg-auto-import-keys ref
run zypper --non-interactive --gpg-auto-import-keys dup -l -y
run zypper --non-interactive --gpg-auto-import-keys install \
lsb-release git tar make cmake gcc gcc-c++ pkg-config \
glibc-devel glib2-devel glib2-tools dbus-1-devel alsa-devel libpulse-devel libnotify-devel \
boost-devel protobuf-devel sqlite3-devel taglib-devel \
gstreamer-devel gstreamer-plugins-base-devel libxine-devel vlc-devel \
libQt5Core-devel libQt5Gui-devel libQt5Widgets-devel libQt5Concurrent-devel libQt5Network-devel libQt5Sql-devel \
libQt5DBus-devel libqt5-qtx11extras-devel libqt5-qtbase-common-devel \
libcdio-devel libgpod-devel libplist-devel libmtp-devel libusbmuxd-devel libchromaprint-devel
run mkdir -p /usr/src/app
workdir /usr/src/app
copy . /usr/src/app

View File

@@ -1,55 +1,70 @@
Strawberry Music Player
:strawberry: Strawberry Music Player [![Build Status](https://travis-ci.org/jonaski/strawberry.svg?branch=master)](https://travis-ci.org/jonaski/strawberry)
[![Donate](https://img.shields.io/badge/Donate-PayPal-green.svg)](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=FRJUYV5QP6HW8)
=======================
README
------
Strawberry is a audio player and music collection organizer. It is a fork of Clementine created in 2013 with a diffrent goal.
It's written in C++ and Qt5. The name is inspired by the band Strawbs.
Strawberry is a audio player and music collection organizer. It is a fork of Clementine released in 2018 aimed at music collectors, audio enthusiasts and audiophiles. The name is inspired by the band Strawbs. It's based on a heavily modified version of Clementine created in 2012-2013. It's written in C++ and Qt 5.
### Features:
* Website: https://www.strawbs.org/
* Github: https://github.com/jonaski/strawberry
* Buildbot: http://buildbot.strawbs.net/
* Latest builds: https://builds.strawbs.org/
### :heavy_check_mark: Features:
* Play and organize music
* Supports WAV, FLAC, WavPack, DSF, DSDIFF, Ogg Vorbis, Speex, MPC, TrueAudio, AIFF, MP4, MP3, ASF and Monkey's Audio.
* Audio CD playback
* Native desktop notifications
* Playlists in multiple formats
* Advanced audio output and device configuration for bit-perfect playback on Linux
* Edit tags on music files
* Fetch tags from MusicBrainz
* Album cover art from Lastfm, Musicbrainz, Discogs and Amazon
* Album cover art from Last.fm, Musicbrainz and Discogs
* Song lyrics from AudD and API Seeds
* Support for multiple backends
* Audio analyzer
* Audio equalizer
* Transfer music to iPod, iPhone, MTP or mass-storage USB player
* Streaming support for Tidal and Deezer
* Scrobbler with support for Last.fm, Libre.fm and ListenBrainz
You can obtain and view the sourcecode on github at: https://github.com/jonaski/strawberry
It has so far been tested to work on Linux, OpenBSD, macOS and Windows.
It has so far been tested on Linux, OpenBSD and cross compiled for Windows. I have not had a chance to test it on Mac OS X since I don't have a mac.
### :heavy_exclamation_mark: Requirements
To build Strawberry from source you need the following installed on your system with the additional development packages/headers:
Requirements
------------
* [GLib, GIO and GObject](https://developer.gnome.org/glib/)
* [POSIX thread (pthread) libraries](http://www.yolinux.com/TUTORIALS/LinuxTutorialPosixThreads.html)
* [CMake and Make tools](https://cmake.org/)
* [GCC](https://gcc.gnu.org/) or [clang](https://clang.llvm.org/) compiler
* [Protobuf library and compiler](https://developers.google.com/protocol-buffers/)
* [Boost development headers](https://www.boost.org/)
* [Qt 5 with components Core, Gui, Widgets, Concurrent, Network and Sql](https://www.qt.io/)
* [Qt 5 components X11Extras and DBus for Linux/BSD, MacExtras for macOS and WinExtras for Windows](https://www.qt.io/)
* [SQLite3](https://www.sqlite.org)
* [TagLib 1.11.1 or higher](http://taglib.org/)
* [Chromaprint library](https://acoustid.org/chromaprint)
* [ALSA library (linux)](https://www.alsa-project.org/)
* [DBus (linux)](https://www.freedesktop.org/wiki/Software/dbus/)
* [PulseAudio (linux optional)](https://www.freedesktop.org/wiki/Software/PulseAudio/?)
* [GStreamer](https://gstreamer.freedesktop.org/), [Xine](https://www.xine-project.org), [VLC](https://www.videolan.org), [Deezer](https://build-repo.deezer.com/native_sdk/deezer-native-sdk-v1.2.10.zip) or [Phonon](https://techbase.kde.org/Phonon)
To build Strawberry from source you need the following installed on your system:
Optional dependencies:
* glib2, glib2-devel, git, cmake, make, gcc and gcc-c++
* protobuf and development packages
* boost development headers
* Audio CD: [libcdio](https://www.gnu.org/software/libcdio/)
* MTP devices: [libmtp](http://libmtp.sourceforge.net/)
* iPod Classic devices: [libgpod](http://www.gtkpod.org/libgpod/)
* iPhone, iPod Touch, iPad and Apple TV devices: [libimobiledevice, libplist and libusbmuxd](https://www.libimobiledevice.org/)
* The following Qt5 components are required with additional development packages: Qt5Core, Qt5Widgets, Qt5Network, Qt5Sql, Qt5Xml, Qt5OpenGL, Qt5Concurrent, Qt5Test, 5X11Extras, Qt5WebKit, Qt5WebKitWidget and Qt5DBus.
Either GStreamer, Xine, VLC, Deezer or Phonon engine is required, but only GStreamer is fully implemented so far.
You should also install the gstreamer plugins base and good, and optionally bad and ugly.
* ALSA and libasound2 with development files
* SQLite3 with development files
* TagLib 1.8 or higher with development files
* libchromaprint with development files
* libglu with development files
Deezer streams with full songs are encrypted and only urls for preview streams (MP3) are exposed by the API.
Full length songs requires the use of deezers own engine (Deezer SDK).
The Deezer SDK can be found here: https://build-repo.deezer.com/native_sdk/deezer-native-sdk-v1.2.10.zip
Either GStreamer, Xine or VLC engine is required, but only GStreamer is fully implemented so far.
You should also install the gstreamer plugins: gstreamer-plugins-base, gstreamer-plugins-good and gstreamer-plugins-bad
* The Qt5 specific LastFM library and development files are required for fetching album covers from LastFM.
* To enable CD support for playing audio cd's you need libcdio.
* If you want MTP support you need libmtp.
* If you need iPod Classic support you need libgpod.
Compiling from source
---------------------
### :wrench: Compiling from source
### Get the code:
@@ -64,5 +79,16 @@ Compiling from source
sudo make install
(dont change to the source directory, if you created the build directory inside the source directory type: cmake .. instead).
### :penguin: Packaging status
[![Packaging status](https://repology.org/badge/vertical-allrepos/strawberry.svg)](https://repology.org/metapackage/strawberry/versions)
### :computer: Screenshot
![Browse](https://www.strawbs.org/pictures/screenshot-002-large.png)
### :moneybag: Donate
[![paypal](https://www.paypalobjects.com/en_US/i/btn/btn_donateCC_LG.gif)](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=FRJUYV5QP6HW8)

6
cmake/Deb.cmake Normal file
View File

@@ -0,0 +1,6 @@
add_custom_target(deb
#COMMAND ${CMAKE_COMMAND} -E create_symlink ${CMAKE_SOURCE_DIR}/dist/debian ${CMAKE_BINARY_DIR}/debian
COMMAND cp -r -p ${CMAKE_SOURCE_DIR}/dist/debian ${CMAKE_BINARY_DIR}/
COMMAND dpkg-buildpackage -b -d -uc -us
WORKING_DIRECTORY ${CMAKE_BINARY_DIR}
)

5
cmake/Dmg.cmake Normal file
View File

@@ -0,0 +1,5 @@
add_custom_target(dmg
COMMAND sudo ${CMAKE_SOURCE_DIR}/dist/macos/macdeploy.py strawberry.app
COMMAND sudo ${CMAKE_SOURCE_DIR}/dist/macos/create-dmg.sh strawberry.app
WORKING_DIRECTORY ${CMAKE_BINARY_DIR}
)

Some files were not shown because too many files have changed in this diff Show More