Compare commits

...

372 Commits

Author SHA1 Message Date
Chris Lee
a82b9a1d7f Release 16.6.1 2018-08-05 21:27:23 +00:00
Chris Lee
89a6cadfcd add patch version number option to release script 2018-08-05 21:27:15 +00:00
Chris Lee
fb9da655df remove configure script from main dir as it breaks debian packaging 2018-08-05 21:12:17 +00:00
Chris Lee
cb137674e4 fix ci script 2018-08-05 09:35:28 +00:00
Chris Lee
1054aac7ca Release 16.6 2018-08-04 18:43:09 +00:00
Chris Lee
81c7c65a9f Fix regression in task icon brightness (issue #714) 2018-08-04 18:41:53 +00:00
Chris Lee
044bad6c40 Release 16.5 2018-08-04 18:06:12 +00:00
Chris Lee
aaa0e40af7 Update changelog 2018-08-04 18:05:59 +00:00
Chris Lee
34f9fcaca4 Update project file 2018-08-04 18:02:32 +00:00
Chris Lee
f8c6dc1ecc Remove warning 2018-08-04 18:02:05 +00:00
Chris Lee
11c468816d Replace notify-send with xmessage for minimalist setups 2018-07-29 22:32:01 +00:00
Chris Lee
595d04451b Replace notify-send with xmessage for minimalist setups 2018-07-29 22:25:42 +00:00
Chris Lee
6681cfbca6 Fix cmake build 2018-05-31 07:16:08 +00:00
Chris Lee
14074894f9 Use python2 in configure script 2018-05-31 06:53:03 +00:00
Chris Lee
3fcbf58873 Add memory allocation tracing 2018-05-31 06:34:41 +00:00
Chris Lee
1c2a12eb05 Prefer libunwind to libbacktrace 2018-05-31 06:34:02 +00:00
Chris Lee
ec5bda6ddf Add option to sort taskbar by application name (update changelog) 2018-05-28 21:24:32 +00:00
Chris Lee
5875015c11 Add option to sort taskbar by application name (contributed by Fabian Carlstrom) 2018-05-28 21:23:05 +00:00
Chris Lee
21e9303502 Fix regression caused by imlib leak (related: issue #704) 2018-05-28 21:20:06 +00:00
Chris Lee
94d4a219ee New ninja-based build script (issue #675) 2018-05-28 18:58:19 +00:00
Chris Lee
8a7ba9bf50 Release 16.4 2018-05-03 04:06:13 +00:00
Chris Lee
ffa6e5ba49 Change new release script to also publish to remote (issue #707) 2018-05-03 03:58:05 +00:00
Chris Lee
d1f5a46a73 Fix executor verbosity (issue #699) 2018-04-17 19:48:55 +00:00
Chris Lee
131704081c Fix imlib leak (issue #704) 2018-04-17 19:37:10 +00:00
Chris Lee
21466cc94e Update authors file 2018-04-17 19:22:06 +00:00
Chris Lee
e06b63e1dc Release 16.3 2018-04-17 19:19:00 +00:00
Chris Lee
9a03b8b4e6 Update changelog 2018-04-17 19:18:50 +00:00
o9000
47bc01d608 Scaling: limit to 8x 2018-02-10 10:52:10 +00:00
o9000
0e4b3bd32e Merge branch 'fix_urgent_delay' into 'master'
Fix one second delay before urgent task starts to blink

See merge request o9000/tint2!30
2018-02-10 10:50:22 +00:00
arza
9ac0cd1254 Fix one second delay before urgent task starts to blink 2018-02-10 07:35:08 +02:00
o9000
73ab9b6930 Merge branch 'master' into 'master'
Fix typo around wiki link

See merge request o9000/tint2!29
2018-02-09 21:28:16 +00:00
Michael Vetter
96361ab1cd Fix typo around wiki link 2018-02-09 13:50:36 +01:00
o9000
b9589450bb Executor: Fix layout (issue #695) 2018-01-26 21:20:23 +00:00
o9000
b810bad93f Release 16.2 2018-01-21 07:04:15 +00:00
o9000
b85362b5c7 Update doc and changelog 2018-01-21 07:03:07 +00:00
o9000
03675b152d Match perfectly text layout and drawing parameters #693 2018-01-20 08:56:58 +00:00
o9000
c3fdd76b58 packaging 2018-01-19 23:23:59 +00:00
o9000
04ba5a6617 Do not spin when battery is unplugged 2018-01-19 23:20:44 +00:00
o9000
f5f8792d42 Update translations 2018-01-19 22:00:26 +00:00
o9000
965a2665b0 Scaling support - scale by screen height (issue #656) 2018-01-19 21:37:56 +00:00
o9000
b2b0119f4d Fix use after free in DPI computation 2018-01-19 21:21:22 +00:00
o9000
41e49ef4e6 Do not spin when battery is unplugged 2018-01-19 21:20:03 +00:00
o9000
25dd623618 Likely fix for #693 2018-01-19 21:19:18 +00:00
o9000
28b1174c0f Remove dead code 2018-01-19 20:49:38 +00:00
o9000
b70edc437c Possible fix for issue #693 2018-01-18 21:41:32 +00:00
o9000
bf58e16b83 Possible fix for issue #693 2018-01-18 21:31:44 +00:00
o9000
3190c5b7de Scaling support - tooltips (issue #656) 2018-01-17 21:12:17 +00:00
o9000
5db0dceec2 Scaling support - doc (issue #656) 2018-01-17 05:49:02 +00:00
o9000
5c5f507e54 Scaling support in tint2conf - translations (issue #656) 2018-01-17 05:40:46 +00:00
o9000
d301ada47a Scaling support in tint2conf - translations (issue #656) 2018-01-17 05:37:15 +00:00
o9000
b3f9834313 Scaling support in tint2conf (issue #656) 2018-01-17 05:27:56 +00:00
o9000
c7b23ee94a Scale panel elements (issue #656) 2018-01-17 05:19:32 +00:00
o9000
12f04e3055 Scale panel window size (issue #656) 2018-01-17 04:13:01 +00:00
o9000
7f62594cf6 Add config option (issue #656) 2018-01-17 03:54:49 +00:00
o9000
405c2c9286 Compute monitor DPI (issue #656) 2018-01-17 03:51:32 +00:00
o9000
90a445868c Update changelog 2018-01-15 06:13:05 +00:00
o9000
427afc61b5 Distribute size correctly between taskbars when center alignment is used and there is only one visible taskbar (Fixes issue #691) 2018-01-14 17:31:46 +00:00
o9000
ee92fac10e Add a timeout to the version checker script 2017-12-31 09:20:15 +00:00
o9000
95eea350fe Add a timeout to the version checker script 2017-12-31 09:15:34 +00:00
o9000
8242b494bc Release 16.1 2017-12-30 09:29:29 +00:00
o9000
1c6377f570 Remove tint2 ppa 2017-12-29 16:28:30 +01:00
o9000
dd1fd28114 Embed default icon, in case it is not found on the system (e.g. tint2 not installed, just compiled) 2017-12-29 16:10:58 +01:00
o9000
b4610fcb6e CI script: remove ANSI color codes 2017-12-29 14:45:47 +01:00
o9000
eb0e6765f9 Taskbar: setup timers in init_() to allow restart 2017-12-29 14:38:06 +01:00
o9000
5ee278d3ca CI script: set a timeout in case unit tests hang 2017-12-29 14:04:02 +01:00
o9000
fb438031c3 Fix CI script 2017-12-29 13:51:04 +01:00
o9000
e5380f8e29 Timer: rename timeout -> timer 2017-12-29 13:10:47 +01:00
o9000
c064ec70ed Fix CI script 2017-12-29 12:53:29 +01:00
o9000
a9a9a753bc Timer: new implementation (merge complete) 2017-12-29 06:32:55 +01:00
o9000
38bee65b58 Timer: new implementation (all tests pass) 2017-12-29 06:21:53 +01:00
o9000
f64cf199e4 Timer: new implementation (merge finished, tests failing) 2017-12-28 22:45:20 +01:00
o9000
0911dcaed1 Timer: new implementation (needs merge) 2017-12-28 21:54:14 +01:00
o9000
c41d75e54e Integrate unit tests in ci script 2017-12-28 19:28:57 +01:00
o9000
8e0bdcaedd Timer: more tests 2017-12-28 13:22:32 +01:00
o9000
be4554b89d Timer: more tests 2017-12-28 11:39:41 +01:00
o9000
82d7fdc8f9 Timer: more tests 2017-12-28 10:42:09 +01:00
o9000
e046cb88ab Timer: more tests 2017-12-28 10:34:53 +01:00
o9000
300ef518cb Timer: more tests 2017-12-28 09:43:00 +01:00
o9000
457d51e267 Task, Button: add a bit of slack in the pango text layout, to avoid wrapping due to rounding errors 2017-12-28 09:14:02 +01:00
o9000
1b48efe738 Executor: add a bit of slack in the pango text layout, to avoid wrapping due to rounding errors 2017-12-28 09:11:43 +01:00
o9000
40e01e2abf Timer: add testing base 2017-12-27 22:18:02 +01:00
o9000
a7ca1b739b Workaround broken timer code (SIGSEGV on recent gcc) 2017-12-27 21:15:12 +01:00
o9000
750cbd572c Cleanup cmakefile 2017-12-21 17:27:05 +01:00
o9000
75c2a2084d get_version.sh: allow running outside source tree 2017-12-21 17:25:33 +01:00
o9000
9b17461f74 Updated changelog 2017-12-21 12:54:25 +01:00
o9000
a185f625f9 Proper fix for time_t printing 2017-12-21 12:33:09 +01:00
o9000
3a9181eff5 Fix packaged version check for OpenBSD 2017-12-21 12:31:17 +01:00
o9000
5cea67171b Do not report 3rd party library warnings 2017-12-21 12:27:57 +01:00
o9000
328a35f949 A bit of refactoring 2017-12-21 12:06:23 +01:00
o9000
cab9c3bddd A bit of refactoring 2017-12-21 12:05:37 +01:00
o9000
5dd814773a A bit of refactoring 2017-12-21 11:57:49 +01:00
o9000
f4384b786c Fix includes in strlcat 2017-12-21 11:47:59 +01:00
o9000
5bc83561e0 Regression tests: use proper OS names 2017-12-21 11:45:57 +01:00
o9000
375e965a3a Silence time_t warning 2017-12-21 11:44:41 +01:00
o9000
67e25b8102 Replace strcat with strlcat 2017-12-21 11:42:07 +01:00
o9000
c96201930b Replace sprintf with snprintf 2017-12-21 11:22:11 +01:00
o9000
6bf72a030a Update changelog 2017-12-20 20:27:28 +01:00
o9000
63d0d98a5c Release 16.0 2017-12-20 20:19:49 +01:00
o9000
e739023529 Set C standard version depending on feature tests 2017-12-20 19:59:35 +01:00
o9000
a7a9c5cdae Regression tests: check compilation on BSD 2017-12-20 19:56:49 +01:00
o9000
50822bd2fd Regression tests: check compilation on BSD 2017-12-20 19:43:23 +01:00
o9000
00c79073f0 Regression tests: check compilation on BSD 2017-12-20 19:32:29 +01:00
o9000
f5b36b37b6 Regression tests: check compilation on BSD 2017-12-20 19:24:18 +01:00
o9000
c635f46439 Feature test C11 generics, since GCC lies about compliance 2017-12-20 18:55:50 +01:00
o9000
16a359f944 Upodate readme 2017-12-20 18:21:14 +01:00
o9000
9a972f4c25 Upodate ide files 2017-12-20 17:51:19 +01:00
o9000
9d06ac0157 Upodate changelog 2017-12-20 16:54:22 +01:00
o9000
cfac6a645d Fix alignment when taskbar_distribute_size = 1 and task_align is not left (issue #688) 2017-12-20 16:47:55 +01:00
o9000
648c7c109f Move files to util 2017-12-20 16:40:23 +01:00
o9000
8946f93254 New unit testing skeleton 2017-12-19 15:32:23 +01:00
o9000
58e030de5d New unit testing skeleton 2017-12-19 15:28:57 +01:00
o9000
c2f8c210f8 gitignore 2017-12-19 12:36:18 +01:00
o9000
978e1c90fc Update translations 2017-11-26 21:45:26 +01:00
o9000
3fba8aa1cf tint2conf: Fix tooltip (issue #588) 2017-11-26 21:40:07 +01:00
o9000
26251849c6 Include also timestamp in version number for unstable builds, so that version numbers increase 2017-11-26 18:38:17 +01:00
o9000
abeb7ce2e6 Include commit hash if possible in versioning script, if a non-stable tree is compiled 2017-11-26 18:28:25 +01:00
o9000
abdb1aeff9 Include commit hash if possible in versioning script, if a non-stable tree is compiled 2017-11-26 18:20:26 +01:00
o9000
7e383c395e Include commit hash if possible in versioning script, if a non-stable tree is compiled 2017-11-26 18:14:46 +01:00
o9000
c3ed2dadf3 Fix issue #684 2017-11-26 17:22:58 +01:00
o9000
bd2ca94ffe Cleanup versioning script 2017-11-26 16:22:12 +01:00
o9000
1753641fc9 Fix issue #684 2017-11-26 16:14:31 +01:00
o9000
467ea1332c Taskbar: thumbnails (cleanup) 2017-11-26 10:24:40 +01:00
o9000
68c2ad7062 Taskbar: thumbnails (disable cairo method as it leaks heavily when screensaver is on) 2017-11-25 22:27:46 +01:00
o9000
cc5842463d Simplify get_text_size 2017-11-23 22:38:55 +01:00
o9000
5e124c7a97 Separator: fix memory leak 2017-11-23 22:37:42 +01:00
o9000
07865142b2 Refactoring 2017-11-23 22:31:19 +01:00
o9000
0c71fda5e1 Button: fix memory leak 2017-11-23 22:28:26 +01:00
o9000
8eaf187984 Implement tinting by icon content (refactoring) 2017-11-23 22:05:41 +01:00
o9000
abe8a0eeb1 Implement tinting by icon content (do not apply task state asb effects) 2017-11-22 09:06:10 +01:00
o9000
d82d782541 Update authors 2017-11-21 13:30:46 +01:00
o9000
715eb556da Taskbar: thumbnails (initialize settings) 2017-11-21 12:51:54 +01:00
o9000
b9e64da9da Update translations 2017-11-21 12:40:27 +01:00
o9000
224b7fba82 Regenerate man page 2017-11-21 12:06:01 +01:00
o9000
d72fff9653 Taskbar: thumbnails (update doc) 2017-11-21 12:05:37 +01:00
o9000
67c3e47414 Implement tinting by icon content (config and documentation) 2017-11-21 12:04:36 +01:00
o9000
e96e7fbee7 Taskbar: thumbnails (fix bad copy paste) 2017-11-20 14:12:26 +01:00
o9000
65c91667f9 Implement tinting by icon content (bugfixes) 2017-11-20 10:15:12 +01:00
o9000
9f4087b471 Implement tinting by icon content (issue #638; thanks @heisenbug) 2017-11-19 22:15:24 +01:00
o9000
247687307b Taskbar: thumbnails (update periodically only the active window) 2017-11-17 17:32:49 +01:00
o9000
2c9d1fdf7d Taskbar: thumbnails (twaked blur a bit) 2017-11-17 17:25:53 +01:00
o9000
500b8f5bea Taskbar: thumbnails (improved fixed point precision) 2017-11-17 17:09:46 +01:00
o9000
7e6f7df55e Taskbar: thumbnails (interleave slow periodic throttling with event processing) 2017-11-17 15:53:23 +01:00
o9000
47201cab84 Taskbar: thumbnails (interleave slow periodic throttling with event processing) 2017-11-17 15:40:59 +01:00
o9000
f7d083904f Timer: do not clear timers restarted from their own callback function - fix check 2017-11-17 15:40:48 +01:00
o9000
f11d30f076 Taskbar: thumbnails (only print debug info if DEBUG_THUMBNAILS environment variable is set) 2017-11-17 15:15:15 +01:00
o9000
c0eaa8274f Taskbar: thumbnails (update every 1s for the active tooltip) 2017-11-17 15:08:34 +01:00
o9000
38488b8d75 Timer: do not clear timers restarted from their own callback function 2017-11-17 15:08:27 +01:00
o9000
07339c09a0 Taskbar: thumbnails (update every 1s for the active tooltip) 2017-11-17 11:45:42 +01:00
o9000
0521223899 Taskbar: thumbnails (interleave slow periodic throttling with event processing) 2017-11-17 08:19:23 +01:00
o9000
cbf3cebbb0 Taskbar: thumbnails (XShmGetImage - avoid leaking in X) 2017-11-16 12:14:09 +01:00
o9000
4b50446a7b Taskbar: thumbnails (use slower but safed XGetImage) 2017-11-16 11:39:44 +01:00
o9000
e8a6c93b28 Fix tooltip use after free 2017-11-16 08:32:19 +01:00
o9000
5e6e1184fe Taskbar: thumbnails (small fixes and disable shm for now) 2017-11-15 21:33:52 +01:00
o9000
89ab1fa6c4 Taskbar: thumbnails (make sure we are using rgb24) 2017-11-15 14:11:39 +01:00
o9000
e597973cd7 Taskbar: thumbnails (optimizations - get rid of XGetPixel calls) 2017-11-15 13:00:27 +01:00
o9000
ebe30774ac Taskbar: thumbnails (make sure window is not minimized) 2017-11-15 12:14:37 +01:00
o9000
d463dcb5b4 Taskbar: thumbnails (optimizations) 2017-11-15 12:10:49 +01:00
o9000
23782a4414 Timer: fix use after free 2017-11-15 12:08:29 +01:00
o9000
1be85e66fe Taskbar: thumbnails (optimizations) 2017-11-15 12:08:12 +01:00
o9000
2fe7efd4fe Taskbar: thumbnails (optimizations) 2017-11-15 11:44:35 +01:00
o9000
1b6fd91611 Taskbar: thumbnails (optimizations) 2017-11-15 11:28:03 +01:00
o9000
5730725762 Taskbar: thumbnails (tint2conf) 2017-11-14 17:51:35 +01:00
o9000
87da8c76cc Taskbar: thumbnails (config, tint2conf and doc) 2017-11-14 17:09:04 +01:00
o9000
e5ecc0c15d Taskbar: thumbnails (use image size without cosidering frame) 2017-11-14 15:41:21 +01:00
o9000
812e306376 Taskbar: thumbnails (use gaussian filtering, seems faster) 2017-11-14 13:53:37 +01:00
o9000
3155a5fc89 Taskbar: thumbnails (profiling) 2017-11-14 13:08:53 +01:00
o9000
bc4af51e82 Taskbar: thumbnails (alignment) 2017-11-14 12:58:42 +01:00
o9000
01de174919 Taskbar: thumbnails (timers) 2017-11-14 12:21:08 +01:00
o9000
7ddb373cb4 Taskbar: thumbnails 2017-11-14 11:04:55 +01:00
o9000
8ba1f26309 Taskbar: thumbnails 2017-11-14 10:48:47 +01:00
o9000
5a867a83c6 Taskbar: thumbnails 2017-11-14 10:34:57 +01:00
o9000
8a7e7e4281 Update changelog 2017-11-10 13:52:21 +01:00
o9000
0e8a6dd961 Systray: warn on duplicate config option systray_name_filter (issue #652) 2017-11-10 13:44:04 +01:00
o9000
c4a0ec4140 Executor: update tooltip documentation (fixes issue #676) 2017-11-10 13:37:41 +01:00
o9000
07d907fc43 Add Spanish translation (contributed by Vicmz) 2017-11-10 13:23:03 +01:00
o9000
b1f83baf04 Update periodic testing scripts 2017-11-05 19:54:17 +01:00
o9000
1ff3404e56 Update periodic testing scripts 2017-11-05 19:51:09 +01:00
o9000
41190204b3 Update periodic testing script 2017-11-05 19:33:09 +01:00
o9000
5bc978ee44 Update periodic testing script 2017-11-05 19:16:02 +01:00
o9000
3b4028f443 Update periodic testing script 2017-11-05 19:14:34 +01:00
o9000
acc3ee9205 Update periodic testing script 2017-11-05 15:38:33 +01:00
o9000
d8770ed590 Release 15.3 2017-11-05 12:28:35 +01:00
o9000
be7873a688 Update release script 2017-11-05 12:28:02 +01:00
o9000
cf81f1c9f9 Update release script 2017-11-05 12:27:05 +01:00
o9000
cde05df1bc Update release script 2017-11-05 12:24:46 +01:00
o9000
5fee459945 Update changelog 2017-11-05 12:19:56 +01:00
o9000
6d67291928 Update release script 2017-11-05 12:18:24 +01:00
o9000
4171e23153 Do not run regression tests more than once for a commit 2017-10-16 11:09:56 +02:00
o9000
725f625aba Use correct whitespace in debian/rules (makefile) 2017-10-15 20:48:28 +02:00
o9000
a6ea1eb5a9 Packaging: Workaround cmake issues 2017-10-15 20:41:46 +02:00
o9000
14b983cd0c Packaging: Workaround cmake issues 2017-10-15 20:30:39 +02:00
o9000
23ddb47e0c Reset signal mask at startup 2017-10-15 13:07:12 +02:00
o9000
407aef3786 Reset signal mask before executing commands (issue #674) 2017-10-15 13:00:18 +02:00
o9000
50c7bf77de Do not hardcode path to /etc 2017-10-04 19:15:44 +02:00
o9000
9a7d5a1a51 Release 15.2 2017-10-01 18:45:36 +02:00
o9000
031bd23849 Print tint2: before geometry dumps 2017-10-01 18:33:53 +02:00
o9000
7e2dc91ce7 Silence warning 2017-10-01 14:04:34 +02:00
o9000
c7a81655c4 Merge branch 'warning' into 'master'
Fix warning ISO C does not support __FUNCTION__

See merge request o9000/tint2!28
2017-10-01 11:54:43 +00:00
fafryd
9aa50104db Fix warning ISO C does not support __FUNCTION__
"warning: ISO C does not support ‘__FUNCTION__’ predefined identifier"

Among others -Wpedantic now warns about non-standard predefined identifiers.
The fix is either to use the standard predefined identifier __func__ (since C99),
or to use the __extension__ keyword.
2017-10-01 09:37:05 +02:00
o9000
3320ee8e05 Regression testing 2017-09-29 10:32:24 +02:00
o9000
ec380e25ec Regression testing 2017-09-28 20:06:39 +02:00
o9000
e1b29d0204 Regression testing 2017-09-28 19:39:30 +02:00
o9000
13313f64c5 Regression testing 2017-09-28 19:18:34 +02:00
o9000
9cb64e9cf5 Regression testing 2017-09-28 19:10:10 +02:00
o9000
fd0a1bef65 Regression testing 2017-09-28 18:51:07 +02:00
o9000
ae9ae098cc Regression testing 2017-09-28 18:47:42 +02:00
o9000
18f49d0d52 Regression testing 2017-09-28 18:39:15 +02:00
o9000
a57b6a4706 Regression testing 2017-09-28 18:38:54 +02:00
o9000
f8aa84a91b Regression testing 2017-09-28 18:36:34 +02:00
o9000
0154fe6a5a Regression testing 2017-09-28 15:59:29 +02:00
o9000
6f96818438 Regression testing 2017-09-28 15:43:12 +02:00
o9000
6433767a98 Regression testing 2017-09-28 15:39:10 +02:00
o9000
f8037b7ff5 Regression testing 2017-09-28 15:19:51 +02:00
o9000
7dddd4a5a2 Regression testing 2017-09-28 15:15:00 +02:00
o9000
85ac37d019 Regression testing 2017-09-28 15:11:31 +02:00
o9000
499b2bd938 Regression testing 2017-09-28 15:09:57 +02:00
o9000
5302fb4ba7 Regression testing 2017-09-28 15:08:26 +02:00
o9000
b58a6512b3 Update changelog 2017-09-28 12:27:26 +02:00
o9000
e38ccf5376 Remember window order on panel restart (issue #615) 2017-09-28 12:26:46 +02:00
o9000
3c9a0ff2f7 Updated changelog 2017-09-28 11:28:17 +02:00
o9000
b8675fa208 Compute text size correctly (issue #671) 2017-09-28 11:23:31 +02:00
o9000
ddac8f7802 Battery: add data gathering script 2017-09-17 15:20:38 +02:00
o9000
c21930de2b Battery: check that data files are readable and non-empty 2017-09-17 14:26:29 +02:00
o9000
f2cc1b68c1 More battery data 2017-09-17 14:12:41 +02:00
o9000
fc61676732 Battery: Do not show time remaining if rate is zero 2017-09-17 14:09:48 +02:00
o9000
1772d0a894 More battery data 2017-09-17 14:04:07 +02:00
o9000
3de424b129 Allow batteries that do not provide the charging rate 2017-09-17 14:04:03 +02:00
o9000
d8c289e0cc More battery data 2017-09-17 13:50:58 +02:00
o9000
5a17bb2fda Update packaging info script 2017-09-12 23:41:06 +02:00
o9000
c45b06657a Update packaging info script 2017-09-12 23:38:21 +02:00
o9000
7666077c63 Update packaging info script 2017-09-12 23:34:53 +02:00
o9000
542aa56840 Update packaging info script 2017-09-12 23:28:49 +02:00
o9000
d2f990366b Update packaging info script 2017-09-12 23:26:27 +02:00
o9000
1480faf32f Update packaging info script 2017-09-12 23:15:12 +02:00
o9000
38ff3318eb Update packaging info script 2017-09-12 23:14:08 +02:00
o9000
5bd253c0da Update packaging info script 2017-09-12 23:07:57 +02:00
o9000
9e94ee15e4 Update packaging info script 2017-09-12 22:56:25 +02:00
o9000
a026cd91fe Update memory reporting script 2017-09-12 22:36:52 +02:00
o9000
a52c45bd08 Remove duplicate entry 2017-09-12 22:36:30 +02:00
o9000
88c91aae25 Update version checker 2017-09-12 22:36:13 +02:00
o9000
b793544cf8 Add memory analysis script 2017-09-11 19:02:02 +02:00
o9000
63c3690fa8 Release 15.1 2017-09-08 18:08:28 +02:00
o9000
e2ece2c35b Updated changelog 2017-09-08 18:08:18 +02:00
o9000
86d6e96f7a Fix build on various architectures 2017-09-08 17:37:40 +02:00
o9000
87e1ccc6bf Fix build on various architectures 2017-09-08 17:37:01 +02:00
o9000
aa355e22f9 Fix build on various architectures 2017-09-08 17:04:10 +02:00
o9000
d21f758158 Fix build on various architectures 2017-09-08 16:35:24 +02:00
o9000
f2741116a7 Fix build on non-x86 architectures 2017-09-05 19:11:13 +02:00
o9000
ea92bf7718 Release 15.0 2017-09-02 15:07:36 +02:00
o9000
e2bbbd7835 Update versioning script 2017-09-02 15:07:29 +02:00
o9000
b38ae3aad3 Update versioning script 2017-09-02 15:06:56 +02:00
o9000
68d8b35ab8 Update versioning script 2017-09-02 15:05:42 +02:00
o9000
5d25b0ae9b Update readme 2017-09-02 14:55:04 +02:00
o9000
8fa4df0076 Update screenshots 2017-09-02 14:33:46 +02:00
o9000
909d5ec139 Update screenshots 2017-09-02 14:31:16 +02:00
o9000
64a0bf67db Update themes 2017-09-02 14:21:59 +02:00
o9000
a9047947b0 Update changelog 2017-09-02 13:08:14 +02:00
o9000
d49adfdef3 Launcher: Support %f and %F 2017-09-02 13:04:01 +02:00
o9000
498b665c8a Launcher: Fix drag and drop 2017-09-02 12:39:40 +02:00
o9000
14c3824632 Launcher: Add support for Terminal=true 2017-09-02 10:53:20 +02:00
o9000
eb93af3622 preprent "tint2:" to all logging messages 2017-09-01 18:23:46 +02:00
o9000
b7691afb8d Print logging output consistently to stderr 2017-09-01 18:11:35 +02:00
o9000
61c61c8844 Warn on primary_monitor_first 2017-09-01 18:06:56 +02:00
o9000
120207f1f9 Fix regression in cli handling 2017-09-01 18:02:35 +02:00
o9000
ceb6a44238 Launcher: fix layout on vertical panels 2017-09-01 11:34:57 +02:00
o9000
6a1b2f0610 Icon lookup: allow debugging enabled by env variable 2017-09-01 11:34:28 +02:00
o9000
95d4d90efd Button: fix parsing of empty config options 2017-09-01 11:32:44 +02:00
o9000
6605a1c3c2 Tracing support 2017-08-31 21:46:04 +02:00
o9000
0c754affd9 Refactoring 2017-08-31 18:55:22 +02:00
o9000
1e45abe988 Refactoring 2017-08-31 18:38:31 +02:00
o9000
6852e25372 Refactoring 2017-08-31 16:42:05 +02:00
o9000
ac8256a96b Regression tests 2017-08-31 11:42:19 +02:00
o9000
0786016436 Regression tests 2017-08-30 22:01:46 +02:00
o9000
f67b5db2f9 Regression tests 2017-08-30 17:42:02 +02:00
o9000
949fbdba14 Regression tests 2017-08-30 17:38:13 +02:00
o9000
558ffee93b Regression tests 2017-08-30 17:27:21 +02:00
o9000
69274ed7f9 Regression tests 2017-08-30 16:55:40 +02:00
o9000
7f7d0a0aa2 Regression tests 2017-08-30 16:40:59 +02:00
o9000
683d49bc71 Regression tests 2017-08-30 16:31:01 +02:00
o9000
da77f89910 Regression tests 2017-08-30 16:25:47 +02:00
o9000
9b9310dc84 Regression tests 2017-08-30 16:10:07 +02:00
o9000
1f94752e12 Regression tests 2017-08-30 16:00:01 +02:00
o9000
016958cb41 Regression tests 2017-08-30 15:50:31 +02:00
o9000
b003a2b1e3 Regression tests 2017-08-30 15:39:34 +02:00
o9000
eef665c896 Regression tests 2017-08-30 15:39:10 +02:00
o9000
e7492047c0 Regression tests 2017-08-30 15:30:06 +02:00
o9000
71db6de14f Regression tests 2017-08-30 15:29:52 +02:00
o9000
246048d8be Regression tests 2017-08-30 15:13:25 +02:00
o9000
3a7fb0971b packaging version check script 2017-08-28 18:50:40 +02:00
o9000
b672357f78 Add packaging version check script 2017-08-28 16:29:49 +02:00
o9000
302d8ec3b2 Add packaging version check script 2017-08-28 16:10:25 +02:00
o9000
cdbf8d6fc5 Add packaging version check script 2017-08-28 16:02:56 +02:00
o9000
f5f9d4983f Update changelog 2017-08-25 17:23:57 +02:00
o9000
2d67984536 Revert to the old autohide style (issues #640, #657) 2017-08-25 17:16:41 +02:00
o9000
e042bc3a17 Fix memory leak in icon themes (possibly related to issue #650) 2017-08-25 16:51:56 +02:00
o9000
297e2a13a2 Refresh clock text immediately if empty (issue #659, regression) 2017-08-25 16:31:49 +02:00
o9000
4df8f475ce Fix resizing of text elements (issue #661) 2017-08-24 23:57:48 +02:00
o9000
638264d874 Update changelog 2017-08-21 14:35:37 +02:00
o9000
21785a1a08 Update authors file 2017-08-21 14:30:45 +02:00
o9000
8e50c20c9d Reinitialize timers correctly when created from their own callbacks 2017-08-21 14:26:20 +02:00
o9000
c5d2ddc156 Merge branch 'master' of https://gitlab.com/o9000/tint2 2017-08-21 13:54:44 +02:00
o9000
e9adb18d58 Merge branch 'aaaz/tint2-master' 2017-08-21 13:49:56 +02:00
o9000
146408655b Merge branch 'master' into 'master'
update clock at the beginning of seconds

See merge request !27
2017-08-21 11:48:25 +00:00
o9000
3b7d583d8f packaging: remove yakkety (reached EOL) 2017-07-22 10:45:05 +02:00
o9000
4a704dde0c packaging: remove precise (reached EOL) 2017-07-22 10:35:39 +02:00
o9000
f5f8423364 panel: Set _NET_WM_PID (fixes issue #651) 2017-07-22 09:22:27 +02:00
o9000
00a5f72857 Create dirs with parents 2017-07-21 12:03:49 +02:00
o9000
f4ec61340f Fix fd leak to children 2017-07-16 10:14:40 +02:00
o9000
a46d22b31f Fix minor memory leak 2017-07-16 09:57:59 +02:00
o9000
7ad8bbe6e7 Fix minor memory leak 2017-07-16 09:50:53 +02:00
aaaz
fcbdd00bce update clock at the beginning of seconds 2017-07-08 16:24:45 -05:00
o9000
491a56db10 Clock: Update correctly after suspend 2017-07-02 23:19:24 +02:00
o9000
cbe31981b3 Executor: fallback to old style tooltips if stderr is empty 2017-06-27 12:42:51 +02:00
o9000
dafe9824e3 Update changelog 2017-06-27 12:32:23 +02:00
o9000
b931066573 Executor: truncate very long tooltips 2017-06-27 12:27:50 +02:00
o9000
f45e107207 Executor: if no user tooltip is set, display stderr output 2017-06-27 12:24:52 +02:00
o9000
ece3bc4d85 Add some new themes, remove redundant ones 2017-06-27 11:04:12 +02:00
o9000
4b9ee685ac Update timers correctly after suspend 2017-06-27 00:53:17 +02:00
o9000
cfc43685a4 ChangeLog is in the same directory of get_version.sh, get it from there 2017-06-26 22:46:17 +02:00
o9000
d05d5f594b Revert change to ubuntu script 2017-06-26 22:36:42 +02:00
o9000
425036adc9 Get version script: allow running without path parameter 2017-06-26 22:34:48 +02:00
o9000
cb174592be Merge branch 'version' into 'master'
Fix finding Version

See merge request !26
2017-06-26 20:29:57 +00:00
Chris Mayo
7a350a5e83 Fix finding ChangeLog when building
Keep the first optional argument of get_version.sh as "--strict" and
make the second the PROJECT_SOURCE_DIR plus trailing slash from
CMakeLists.txt
2017-06-26 19:23:58 +01:00
Chris Mayo
afadf3ea3f Revert "Use double quoting in get_version.sh"
Fixed symptom not the underlying problem.

This reverts commit e39a841f6e.
2017-06-26 19:23:58 +01:00
o9000
c8ccf053ff tint2conf: fix segfault in corner case 2017-06-26 11:08:31 +02:00
o9000
2f74250634 Add some new themes, remove redundant ones 2017-06-26 11:01:07 +02:00
o9000
ad7faaab81 Make get_version.sh work in the current dir instead of build 2017-06-26 10:23:50 +02:00
o9000
e39a841f6e Use double quoting in get_version.sh 2017-06-26 10:19:08 +02:00
o9000
fc7c6afa81 Updated man page 2017-06-25 13:02:07 +02:00
o9000
51bc9d1569 Updated translation files 2017-06-22 22:11:06 +02:00
o9000
747bbd7c7b Update changelog 2017-06-22 20:43:13 +02:00
o9000
521ffbfaaf Remove primary_monitor_first and add primary as a possible monitor value (issue #614) 2017-06-22 19:57:23 +02:00
o9000
cd33e5b274 Battery: better Unknown state handling 2017-06-22 19:03:05 +02:00
o9000
201ea843a0 Battery: new config option battery_full_cmd (fixes issue #585) 2017-06-20 20:38:26 +02:00
o9000
dee210ceec Updated French translation 2017-06-20 20:25:07 +02:00
o9000
ea0e52c5a2 Battery format fixes 2017-06-20 18:58:22 +02:00
o9000
b5578ff5bc battery: Do not redefine strncat with different semantics 2017-06-20 16:18:28 +02:00
o9000
f9ad3cb029 Merge battery format from https://gitlab.com/berkley4/tint2 (issue #229) 2017-06-20 13:15:41 +02:00
o9000
30c24c59ed Hide executor if output is empty (issue #648) 2017-06-20 13:01:09 +02:00
o9000
6f05c9d327 Hide executor if output is empty (issue #648) 2017-06-20 12:57:35 +02:00
o9000
30429b88b3 Merge battery format from https://gitlab.com/berkley4/tint2 (issue #229) 2017-06-20 12:36:04 +02:00
o9000
394be61a65 Collapse executor if output is empty (fixes issue #648) 2017-06-20 11:38:37 +02:00
o9000
d55f6d7baa taskbar_hide_different_desktop 2017-06-20 05:06:37 +02:00
o9000
2dfcab170d Update packaging 2017-06-11 13:30:27 +02:00
o9000
59b2094ad1 Update packaging 2017-06-11 11:28:04 +02:00
o9000
8046600a55 Release 0.14.6 2017-06-11 11:15:36 +02:00
o9000
17a655b52a Update changelog 2017-06-11 11:14:57 +02:00
o9000
75b8587216 Update changelog 2017-06-05 17:07:04 +02:00
o9000
7c87ab88bc Reexecute on SIGUSR2 2017-06-05 16:56:40 +02:00
o9000
b87470a284 Update packaging script 2017-06-05 16:21:14 +02:00
o9000
948bf10bad Refactor text rendering 2017-06-05 16:12:51 +02:00
o9000
59c3761455 Refactor text rendering 2017-06-05 16:12:51 +02:00
o9000
b50f5ecf3e executor: do not output last line if it is not terminated by newline 2017-06-02 20:10:41 +02:00
o9000
be9c492406 Update changelog 2017-05-31 23:13:55 +02:00
o9000
85e1a356fe Fix incorrect config warning 2017-05-31 23:06:47 +02:00
o9000
5da4c1653a Clock: take into account borders and padding when computing size 2017-05-29 23:57:38 +02:00
o9000
64edd55add Task: take into account border width when computing text height 2017-05-28 14:46:50 +02:00
o9000
bd28ee77d9 Fix task icon size limits (https://forums.bunsenlabs.org/viewtopic.php?pid=51884) 2017-05-24 21:07:47 +02:00
o9000
d10a505aa9 Release 0.14.5 2017-05-21 13:33:11 +02:00
o9000
77f744eba4 Update changelog 2017-05-21 13:32:49 +02:00
o9000
f8dde00a33 Do not pollute command line of executed processes 2017-05-20 14:48:25 +02:00
o9000
380f260027 Fix some memory leaks (issue #641) 2017-04-30 20:49:08 +02:00
o9000
6df4eb4bee Release 0.14.4 2017-04-29 13:47:21 +02:00
o9000
18fcc4952d Update release script 2017-04-29 13:47:04 +02:00
o9000
3878bd6a49 Update changelog 2017-04-29 13:44:22 +02:00
o9000
067234e9fb Unify code used to execute external programs 2017-04-29 13:42:37 +02:00
o9000
e32d2342a6 Fix regression in executor (issue #639) 2017-04-27 21:57:45 +02:00
o9000
82776df9d6 Fix crash when _NET_WM_ICON is set but empty 2017-04-27 09:04:16 +02:00
o9000
f351b6fd8e Release 0.14.3 2017-04-23 17:33:49 +02:00
o9000
a7e4cf42e2 Release script 2017-04-23 17:33:38 +02:00
o9000
a4542e907e Changelog 2017-04-23 17:30:12 +02:00
o9000
12e3fd64bb Release script 2017-04-23 17:29:03 +02:00
246 changed files with 19752 additions and 9802 deletions

4
.gitignore vendored
View File

@@ -1,3 +1,7 @@
build
*.user
version.h
*.pyc
*.todo
packaging/make_ubuntu2.sh
test_*.log

View File

@@ -6,6 +6,7 @@ tint2 is developped by :
- Ovidiu M <mrovi9000 at gmail.com> : launcher, bug fixes
- Mishael A Sibiryakov (death@junki.org) : freespace
- Sebastian Reichel <sre@ring0.de> : battery, various fixes, debian package maintainer
- Chris Lee <chrlee at protonmail> : bug fixes, maintainer starting with v16.3
tint2 is based on the ttm source code (http://code.google.com/p/ttm/)
- 2007-2008 Pål Staurland <staura@gmail.com>
@@ -29,6 +30,10 @@ Contributors:
Oskari Rauta : separator plugin, gradients
Michael Messmore : Support for Path in .desktop files
Matthew Otnel : config option systray_name_filter
Ryan Gray, Jeff Blake (https://gitlab.com/berkley4) : battery format
aaaz (https://gitlab.com/aaaz) : clock fixes
heisenbug (https://gitlab.com/heisenbugh) : taskbar button tinting with icon color
Fabian Carlström : taskbar sort order by app name
Translations:
Bosnian:
@@ -43,3 +48,5 @@ Translations:
Daniel Napora <napcok@gmail.com>
Serbian:
Dino Duratović <dinomol@mail.com>
Spanish:
Vic <vicmz@yandex.com>

View File

@@ -6,6 +6,7 @@ option( ENABLE_TINT2CONF "Enable tint2conf build, a GTK+2 theme configurator for
option( ENABLE_EXTRA_THEMES "Install additional tint2 themes" ON )
option( ENABLE_RSVG "Rsvg support (launcher only)" ON )
option( ENABLE_SN "Startup notification support" ON )
option( ENABLE_TRACING "Build tint2 with tracing instrumentation" OFF )
option( ENABLE_ASAN "Build tint2 with AddressSanitizer" OFF )
option( ENABLE_BACKTRACE "Dump a backtrace in case of fatal errors (e.g. X11 I/O error)" OFF )
option( ENABLE_BACKTRACE_ON_SIGNAL "Dump a backtrace also when receiving signals such as SIGSEGV" OFF )
@@ -24,7 +25,7 @@ endif()
include( FindPkgConfig )
include( CheckLibraryExists )
include( CheckCSourceCompiles )
pkg_check_modules( X11 REQUIRED x11 xcomposite xdamage xinerama xrender xrandr>=1.3 )
pkg_check_modules( X11 REQUIRED x11 xcomposite xdamage xinerama xext xrender xrandr>=1.3 )
pkg_check_modules( PANGOCAIRO REQUIRED pangocairo )
pkg_check_modules( PANGO REQUIRED pango )
pkg_check_modules( CAIRO REQUIRED cairo )
@@ -61,6 +62,17 @@ else()
set(BACKTRACE_L_FLAGS "")
endif()
check_c_source_compiles(
"#define print(x) _Generic((x), default : print_unknown)(x) \n void print_unknown(){} \n int main () { print(0); }"
HAS_GENERIC)
if(HAS_GENERIC)
add_definitions(-DHAS_GENERIC)
set(CSTD "c11")
else()
set(CSTD "c99")
endif(HAS_GENERIC)
if( ENABLE_RSVG )
pkg_check_modules( RSVG librsvg-2.0>=2.14.0 )
endif( ENABLE_RSVG )
@@ -83,10 +95,10 @@ if( NOT IMLIB_BUILD_WITH_X )
endif( NOT IMLIB_BUILD_WITH_X )
add_definitions( -D_BSD_SOURCE -D_DEFAULT_SOURCE -D_WITH_GETLINE )
if(CMAKE_SYSTEM_NAME STREQUAL "Linux")
add_definitions( -D_POSIX_C_SOURCE=200809L -D_BSD_SOURCE -D_DEFAULT_SOURCE )
else(CMAKE_SYSTEM_NAME STREQUAL "Linux")
add_definitions( -D_WITH_GETLINE )
add_definitions( -D_POSIX_C_SOURCE=200809L )
endif(CMAKE_SYSTEM_NAME STREQUAL "Linux")
@@ -115,8 +127,14 @@ include_directories( ${PROJECT_BINARY_DIR}
set( SOURCES src/config.c
src/panel.c
src/server.c
src/tint.c
src/util/server.c
src/main.c
src/init.c
src/util/signals.c
src/util/tracing.c
src/mouse_actions.c
src/drag_and_drop.c
src/default_icon.c
src/clock/clock.c
src/systray/systraybar.c
src/launcher/launcher.c
@@ -134,12 +152,18 @@ set( SOURCES src/config.c
src/separator/separator.c
src/tint2rc.c
src/util/area.c
src/util/bt.c
src/util/common.c
src/util/fps_distribution.c
src/util/strnatcmp.c
src/util/timer.c
src/util/cache.c
src/util/color.c
src/util/strlcat.c
src/util/print.c
src/util/gradient.c
src/util/test.c
src/util/uevent.c
src/util/window.c )
if( ENABLE_BATTERY )
@@ -180,7 +204,6 @@ endif( ENABLE_SN)
if( ENABLE_UEVENT )
add_definitions( -DENABLE_UEVENT )
set( SOURCES ${SOURCES} src/util/uevent.c)
endif( ENABLE_UEVENT )
if(ENABLE_BACKTRACE)
@@ -210,14 +233,23 @@ if( ENABLE_TINT2CONF )
endif( ENABLE_TINT2CONF )
if( ENABLE_ASAN )
SET(ASAN_C_FLAGS " -O0 -g3 -gdwarf-2 -fsanitize=address -fno-common -fno-omit-frame-pointer -rdynamic -Wshadow")
SET(ASAN_L_FLAGS " -O0 -g3 -gdwarf-2 -fsanitize=address -fno-common -fno-omit-frame-pointer -rdynamic -fuse-ld=gold ")
SET(ASAN_C_FLAGS " -O0 -g3 -fsanitize=address -fno-common -fno-omit-frame-pointer -rdynamic -Wshadow")
SET(ASAN_L_FLAGS " -O0 -g3 -fsanitize=address -fno-common -fno-omit-frame-pointer -rdynamic -fuse-ld=gold ")
else()
SET(ASAN_C_FLAGS "")
SET(ASAN_L_FLAGS "")
endif()
add_custom_target( version ALL "${PROJECT_SOURCE_DIR}/get_version.sh" "\"${PROJECT_SOURCE_DIR}\"" )
if( ENABLE_TRACING )
add_definitions( -DHAVE_TRACING )
SET(TRACING_C_FLAGS " -finstrument-functions -finstrument-functions-exclude-file-list=tracing.c -finstrument-functions-exclude-function-list=get_time,gettime -O0 -g3 -fno-common -fno-omit-frame-pointer -rdynamic")
SET(TRACING_L_FLAGS " -O0 -g3 -fno-common -fno-omit-frame-pointer -rdynamic")
else()
SET(TRACING_C_FLAGS "")
SET(TRACING_L_FLAGS "")
endif()
add_custom_target( version ALL "${PROJECT_SOURCE_DIR}/get_version.sh" )
link_directories( ${X11_LIBRARY_DIRS}
${PANGOCAIRO_LIBRARY_DIRS}
@@ -251,13 +283,13 @@ endif( RT_LIBRARY )
target_link_libraries( tint2 m )
add_dependencies( tint2 version )
set_target_properties( tint2 PROPERTIES COMPILE_FLAGS "-Wall -Wpointer-arith -fno-strict-aliasing -pthread -std=c99 ${ASAN_C_FLAGS}" )
set_target_properties( tint2 PROPERTIES LINK_FLAGS "-pthread -fno-strict-aliasing ${ASAN_L_FLAGS} ${BACKTRACE_L_FLAGS}" )
set_target_properties( tint2 PROPERTIES COMPILE_FLAGS "-Wall -Wpointer-arith -fno-strict-aliasing -pthread -std=${CSTD} ${ASAN_C_FLAGS} ${TRACING_C_FLAGS}" )
set_target_properties( tint2 PROPERTIES LINK_FLAGS "-pthread -fno-strict-aliasing ${ASAN_L_FLAGS} ${BACKTRACE_L_FLAGS} ${TRACING_L_FLAGS}" )
install( TARGETS tint2 DESTINATION bin )
install( FILES tint2.svg DESTINATION ${CMAKE_INSTALL_DATADIR}/icons/hicolor/scalable/apps )
install( FILES tint2.desktop DESTINATION ${CMAKE_INSTALL_DATADIR}/applications )
install( FILES themes/tint2rc DESTINATION /etc/xdg/tint2 )
install( FILES themes/tint2rc DESTINATION ${CMAKE_INSTALL_FULL_SYSCONFDIR}/xdg/tint2 )
install( FILES default_icon.png DESTINATION ${CMAKE_INSTALL_DATADIR}/tint2 )
install( FILES AUTHORS ChangeLog README.md doc/tint2.md DESTINATION ${docdir} )
install( FILES doc/manual.html doc/readme.html DESTINATION ${htmldir} )

137
ChangeLog
View File

@@ -1,4 +1,128 @@
2017-04-23 0.14.2
2018-08-05 16.6.1
- Fix packaging regression for debian (issue #715)
2018-08-04 16.6
- Fix regression in task icon brightness (issue #714)
2018-08-04 16.5
- Add new build script
- Add option to sort taskbar by application name
- Fix regression in image loading
2018-05-03 16.4
- Update AUTHORS
- Fixes:
- Fix leak in image loading (issue #704)
- Executors no longer log unless env var DEBUG_EXECUTORS is set
2018-04-17 16.3
- Fixes:
- Layout in executor (issue #695)
- Other small issues
2018-01-21 16.2
- Fixes:
- Proper fix for issue #688
- Fix bad word wrapping (issue #693)
- Enhancements:
- Preliminary high DPI support
2017-12-30 16.1
- Fixes:
- Fixed several use-after-free errors in the timer code
- Merged patches and fixed other warnings on OpenBSD
- Task, Button, Executor: add a bit of slack in the pango text layout,
to avoid wrapping due to rounding errors
2017-12-20 16.0
- Fixes:
- Taskbar: `taskbar_distribute_size = 1` now playes well with `task_align = center` and
`task_align = right` (issue #688)
- Enhancements:
- Added Spanish translation (contributed by Vicmz)
- Executor: updated tooltip documentation (issue #676)
- Systray: warn on duplicate config option systray_name_filter (issue #652)
- Taskbar: thumbnail support in tooltips
- Use C11 if possible to support generic printing for unit tests (should fall back to C99)
2017-11-05 15.3
- Fixes:
- Launcher: Reset signal mask before executing commands (issue #674)
- cmake: Do not hardcode path to /etc
2017-10-01 15.2
- Fixes:
- Battery info is now again displayed even when current sensor is missing (https://github.com/jmc-88/tint3/issues/34)
- Text elements compute their size correctly (issue #671)
- Window order persists on panel restart (issue #615)
2017-09-08 15.1
- Fixes:
- Fixed build on non-Linux and non-x86 systems
2017-09-02 15.0
- Fixes:
- Clock, executors and other timers fire correctly after waking up from suspend
- One-shot timers are restarted correctly from their own callbacks
- Clock is refreshed with better accuracy (thanks @aaaz)
- Panel: by popular demand, the old struts behavior with autohide has been restored (issue #619);
if you encounter applications that interact poorly with it,
you might have better luck with strut_policy = minimum,
if that does not work, you will have to turn off autohide.
- Enhancements:
- Panel:
- _NET_WM_PID is set correctly, so now tint2 can be interacted with more easily from wmctrl and similar apps
- Taskbar: new config option taskbar_hide_different_desktop
- Battery:
- New config option bat1_format and bat2_format
- New config option battery_full_cmd
- Better "Unknown" state handling
- Executor:
- Hide if output is empty
- If no user tooltip is set, displays the script standard error as tooltip.
Tooltip is multiline, can be cleared with the VT100 clear screen sequence, in shell: (>&2 echo -en "\033[2J").
Long tooltips are truncated to 4096 characters.
- Launcher:
- Drag and drop now handles correctly text/uri-list
- Support for Terminal=true
- Support for %f and %F
- Configuration changes:
- Removed primary_monitor_first as it was conflicting with taskbar behavior; use *_monitor = primary instead.
- Other:
- Major code refactoring
- Dropping "0." from the version number and no longer using semver ("Breaking.Feature.Fix").
- Tint2 will always strive to be backwards compatible with respect to the configuration format.
- Very few configurations changes have been broken between 2010 (0.10) - 2017 (0.14);
in all cases they were minor options that caused incorrect behavior,
and the changes were described better by "Feature" or "Fix".
- Practically all releases starting from 0.10 have been very stable,
so there is no point in staying in "0." anymore.
- But I don't want the project to get stuck in "1." forever.
- The new versioning scheme is the following:
- Version numbers will have the format "Feature.Fix", where:
- "Feature" is increased when significant new features are added.
- "Fix" is increased for bugfixes or minor changes.
- 0.14.6 will be followed by 15.0.
2017-06-11 0.14.6
- Fixes:
- Take into account border width when computing text height
- Taskbar: Fix task icon size limits
- Executor: Do not output last line if it is not terminated by newline
- Enhancements:
- Re-execute tint2 on SIGUSR2.
This is useful for preserving config options and environment when updating tint2.
2017-05-21 0.14.5
- Fixes:
- Fixed a couple of memory leaks
2017-04-29 0.14.4
- Fixes:
- Fix regression in executor (issue #639)
- Fix crash when _NET_WM_ICON is set but empty (https://github.com/jmc-88/tint3/issues/21)
2017-04-23 0.14.3
- Fixes:
- Make versioning more robust when building as package
- Enhancements:
@@ -884,3 +1008,14 @@ released tint-0.2
2008-04-22
- fork ttm projet from p://code.google.com/p/ttm/ (by Pål Staurland staura@gmail.com)
while the projet is no longer in developpement, have not changed the name of 'tint'.
.
.
.
.
.
.
.
.
.
.
.

View File

@@ -1,5 +1,5 @@
# Latest stable release: 0.14.1
Changes: https://gitlab.com/o9000/tint2/blob/0.14.1/ChangeLog
# Latest stable release: 16.6.1
Changes: https://gitlab.com/o9000/tint2/blob/16.6.1/ChangeLog
Documentation: [doc/tint2.md](doc/tint2.md)
@@ -8,7 +8,7 @@ Compile it with (after you install the [dependencies](https://gitlab.com/o9000/t
```
git clone https://gitlab.com/o9000/tint2.git
cd tint2
git checkout 0.14.1
git checkout 16.6.1
mkdir build
cd build
cmake ..
@@ -61,9 +61,9 @@ tint2 is a simple panel/taskbar made for modern X window managers. It was specif
# Known issues
* Graphic glitches on Intel graphics cards can be avoided by changing the acceleration method to UXA ([issue 595](https://gitlab.com/o9000/tint2/issues/595))
* Window managers that do not follow exactly the EWMH specification might not interact well with tint2 (known issues for [awesome](https://gitlab.com/o9000/tint2/issues/385), [bspwm](https://gitlab.com/o9000/tint2/issues/524). [openbox-multihead](https://gitlab.com/o9000/tint2/issues/456))
* Full transparency requires a compositor such as Compton (if not provided already by the window manager, as in Compiz/Unity, KDE or XFCE)
* Graphical glitches on Intel graphics cards can be avoided by changing the acceleration method to UXA ([issue 595](https://gitlab.com/o9000/tint2/issues/595))
* Window managers that do not follow exactly the EWMH specification might not interact well with tint2 ([issue 627](https://gitlab.com/o9000/tint2/issues/627)).
* Full transparency requires a compositor such as Compton (if not provided already by the window manager, as in Compiz/Unity, KDE or XFCE).
# How can I help out?
@@ -85,7 +85,7 @@ tint2 is a simple panel/taskbar made for modern X window managers. It was specif
## Various configs:
![screenshot](https://gitlab.com/o9000/tint2/wikis/screenshot.png)
* [Screenshots](https://gitlab.com/o9000/tint2/wikis/screenshots)
## Demos
@@ -93,3 +93,7 @@ tint2 is a simple panel/taskbar made for modern X window managers. It was specif
* [Executor](https://gitlab.com/o9000/tint2/wikis/whats-new-0.12.4.gif)
* [Mouse over effects](https://gitlab.com/o9000/tint2/wikis/whats-new-0.12.3.gif)
* [Distribute size between taskbars, freespace](https://gitlab.com/o9000/tint2/wikis/whats-new-0.12.gif)
## More
* [Tint2 wiki](https://gitlab.com/o9000/tint2/wikis/Home)

View File

@@ -270,6 +270,8 @@ Try to respect as much as possible the order of the options as given below.</p><
<li><code>color</code> is specified in hex RGB, e.g. #ff0000 is red</li>
<li><code>opacity</code> varies from (0 to 100), where 0 is fully transparent, 100 is fully opaque</li>
</ul></li>
<li><p><code>border_content_tint_weight = integer</code> : Mixes the border color with the content color (for tasks, this is the average color of the window icon). Values must be between 0 (no mixing) and 100 (fully replaces the color). <em>(since 16.0)</em></p></li>
<li><p><code>background_content_tint_weight = integer</code> : Mixes the background color with the content color (for tasks, this is the average color of the window icon). Values must be between 0 (no mixing) and 100 (fully replaces the color). <em>(since 16.0)</em></p></li>
</ul>
<p>You can define as many backgrounds as you want. For example, the following config defines two backgrounds:</p><pre class="highlight plaintext"><code>rounded = 1
border_width = 0
@@ -364,12 +366,12 @@ gradient_id_pressed = 2
<li><code>:</code> adds a separator. You can specify more than one. <em>(since 0.13.0)</em></li>
</ul>
<p>For example, <code>panel_items = STC</code> will show the systray, the taskbar and the clock (from left to right).</p></li>
<li><p><code>panel_monitor = monitor (all or 1 or 2 or ...)</code> : Which monitor tint2 draws the panel on</p>
<li><p><code>panel_monitor = monitor (all or primary or 1 or 2 or ...)</code> : Which monitor tint2 draws the panel on</p>
<ul>
<li>The first monitor is <code>1</code></li>
<li>Use <code>panel_monitor = all</code> to get a separate panel per monitor</li>
</ul></li>
<li><p><code>primary_monitor_first = boolean (0 or 1)</code> : Place the primary monitor before all the other monitors in the list. <em>(since 0.12.4)</em></p></li>
<li><p><code>primary_monitor_first = boolean (0 or 1)</code> : Place the primary monitor before all the other monitors in the list. <em>(since 0.12.4; removed in 1.0, use <code>primary</code> instead)</em></p></li>
</ul>
<p><img src="images/panel_padding.jpg" alt=""></p>
<ul>
@@ -384,6 +386,8 @@ gradient_id_pressed = 2
<li><code>width</code> and <code>height</code> can be specified without units (e.g. <code>123</code>) as pixels, or followed by <code>%</code> as percentages of the monitor size (e.g. <code>50%</code>). Use <code>100%</code> for full monitor width/height.
Example:</li>
</ul></li>
<li><p><code>scale_relative_to_dpi = integer</code> : If set to a non-zero value, HiDPI scaling is enabled. Each panel is visible on a different monitor. Thus each panel has a specific scaling factor. The scaling factor is computed as the ratio between the monitor DPI (obtained from the dimensions in pixels and millimeters from RandR) and a configured reference DPI - this is the DPI for which exising user configs looked normal, for backward compatibility.</p></li>
<li><p><code>scale_relative_to_screen_height = integer</code> : Similar to <code>scale_relative_to_dpi</code>, except the scaling factor is computed as the ratio between the monitor height and <code>scale_relative_to_screen_height</code>. The effect is cumulative with <code>scale_relative_to_dpi</code>, i.e. if both options are present, the factors are multiplied.</p></li>
</ul>
<pre class="highlight plaintext"><code># The panel's width is 94% the size of the monitor, the height is 30 pixels:
panel_size = 94% 30
@@ -456,11 +460,13 @@ panel_size = 94% 30
<li><p><code>taskbar_active_background_id = integer</code> : Which background to use for the taskbar of the current virtual desktop.</p></li>
<li><p><code>taskbar_hide_inactive_tasks = boolean (0 or 1)</code> : If enabled, the taskbar shows only the active task. <em>(since 0.12)</em></p></li>
<li><p><code>taskbar_hide_different_monitor = boolean (0 or 1)</code> : If enabled, the taskbar shows only the tasks from the current monitor. Useful when running different tint2 instances on different monitors, each one having its own config. <em>(since 0.12)</em></p></li>
<li><p><code>taskbar_hide_different_desktop = boolean (0 or 1)</code> : If enabled, the taskbar shows only the tasks from the current desktop. Useful to make multi-desktop taskbars more compact, but still allow desktop switching with mouse click. <em>(since 1.0)</em></p></li>
<li><p><code>taskbar_always_show_all_desktop_tasks = boolean (0 or 1)</code> : Has effect only if <code>taskbar_mode = multi_desktop</code>. If enabled, tasks that appear on all desktops are shown on all taskbars. Otherwise, they are shown only on the taskbar of the current desktop. <em>(since 0.12.4)</em></p></li>
<li><p><code>taskbar_sort_order = none/title/center</code> : Specifies the sort order of the tasks on the taskbar. <em>(since 0.12)</em></p>
<ul>
<li><code>none</code> : No sorting. New tasks are simply appended at the end of the taskbar when they appear.</li>
<li><code>title</code> : Sorts the tasks by title.</li>
<li><code>application</code> : Sorts the tasks by application name. <em>(since 16.3)</em></li>
<li><code>center</code> : Sorts the tasks by their window centers.</li>
<li><code>mru</code> : Shows the most recently used tasks first. <em>(since 0.12.4)</em></li>
<li><code>lru</code> : Shows the most recently used tasks last. <em>(since 0.12.4)</em></li>
@@ -480,6 +486,8 @@ panel_size = 94% 30
<li><p><code>task_text = boolean (0 or 1)</code> : Whether to display the task text.</p></li>
<li><p><code>task_centered = boolean (0 or 1)</code> : Whether the task text is centered.</p></li>
<li><p><code>task_tooltip = boolean (0 or 1)</code> : Whether to show tooltips for tasks.</p></li>
<li><p><code>task_thumbnail = boolean (0 or 1)</code> : Whether to show thumbnail tooltips for tasks. <em>(since 16.0)</em></p></li>
<li><p><code>task_thumbnail_size = width</code> : Thumbnail size. <em>(since 16.0)</em></p></li>
<li><p><code>task_maximum_size = width height</code></p>
<ul>
<li><code>width</code> is used with horizontal panels to limit the size of the tasks. Use <code>width = 0</code> to get full taskbar width.</li>
@@ -523,7 +531,7 @@ panel_size = 94% 30
<li><p><code>systray_sort = ascending/descending/left2right/right2left</code> : Specifies the sorting order for the icons in the systray: in ascending/descending alphabetical order of the icon title, or always add icons to the right/left (note that with <code>left2right</code> or <code>right2left</code> the order can be different on panel restart).</p></li>
<li><p><code>systray_icon_size = max_icon_size</code> : Set the maximum system tray icon size to <code>number</code>. Set to <code>0</code> for automatic icon sizing.</p></li>
<li><p><code>systray_icon_asb = alpha (0 to 100) saturation (-100 to 100) brightness (-100 to 100)</code> : Adjust the systray icons color and transparency.</p></li>
<li><p><code>systray_monitor = integer (1, 2, ...)</code> : On which monitor to draw the systray. The first monitor is <code>1</code>. <em>(since 0.12)</em></p></li>
<li><p><code>systray_monitor = integer (1, 2, ...) or primary</code> : On which monitor to draw the systray. The first monitor is <code>1</code>. <em>(since 0.12)</em></p></li>
<li><p><code>systray_name_filter = string</code> : Regular expression to identify icon names to be hidden. For example, <code>^audacious$</code> will hide icons with the exact name <code>audacious</code>, while <code>aud</code> will hide any icons having <code>aud</code> in the name. <em>(since 0.13.1)</em></p></li>
</ul>
<h3 id="clock">Clock<a name="clock" href="#clock" class="md2man-permalink" title="permalink"></a></h3>
@@ -565,10 +573,20 @@ panel_size = 94% 30
<ul>
<li><p><code>battery_hide = never/integer (0 to 100)</code> : At what battery percentage the battery item is hidden.</p></li>
<li><p><code>battery_low_status = integer</code>: At what battery percentage the low command is executed.</p></li>
<li><p><code>battery_low_cmd = notify-send &quot;battery low&quot;</code> : Command to execute when the battery is low.</p></li>
<li><p><code>battery_low_cmd = xmessage &#39;tint2: Battery low!&#39;</code> : Command to execute when the battery is low.</p></li>
<li><p><code>battery_full_cmd = notify-send &quot;battery full&quot;</code> : Command to execute when the battery is full.</p></li>
<li><p><code>bat1_font = [FAMILY-LIST] [STYLE-OPTIONS] [SIZE]</code></p></li>
<li><p><code>bat2_font = [FAMILY-LIST] [STYLE-OPTIONS] [SIZE]</code></p></li>
<li><p><code>battery_font_color = color opacity (0 to 100)</code></p></li>
<li><p><code>bat1_format = FORMAT_STRING</code> : Format for battery line 1. Default: %p. <em>(since 1.0)</em> Format specification:</p>
<ul>
<li>%s: State (charging, discharging, full, unknown).</li>
<li>%m: Minutes left until completely charged/discharged (estimated).</li>
<li>%h: Hours left until completely charged/discharged (estimated).</li>
<li>%t: Time left. Shows &quot;hrs:mins&quot; when charging/discharging, or &quot;Ful\&quot; when full.</li>
<li>%p: Percentage. Includes the % sign.</li>
</ul></li>
<li><p><code>bat2_format = FORMAT_STRING</code> : Format for battery line 2. Default: %t. <em>(since 1.0)</em></p></li>
<li><p><code>battery_padding = horizontal_padding vertical_padding</code></p></li>
<li><p><code>battery_background_id = integer</code> : Which background to use for the battery.</p></li>
<li><p><code>battery_tooltip_enabled = boolean (0 or 1)</code> : Enable/disable battery tooltips. <em>(since 0.12.3)</em></p></li>
@@ -590,7 +608,7 @@ panel_size = 94% 30
<li><p><code>execp_cache_icon = boolean (0 or 1)</code> : If <code>execp_cache_icon = 0</code>, the image is reloaded each time the command is executed (useful if the image file is changed on disk by the program executed by <code>execp_command</code>). <em>(since 0.12.4)</em></p></li>
<li><p><code>execp_icon_w = integer</code> : You can use <code>execp_icon_w</code> and <code>execp_icon_h</code> to resize the image. If one of them is zero/missing, the image is rescaled proportionally. If both of them are zero/missing, the image is not rescaled. <em>(since 0.12.4)</em></p></li>
<li><p><code>execp_icon_h = integer</code> : See <code>execp_icon_w</code>. <em>(since 0.12.4)</em></p></li>
<li><p><code>execp_tooltip = text</code> : The tooltip. Leave it empty to not display a tooltip. Not specifying this option leads to showing an automatically generated tooltip with information about when the command was last executed. <em>(since 0.12.4)</em></p></li>
<li><p><code>execp_tooltip = text</code> : The tooltip. If left empty, no tooltip is displayed. If missing, the standard error of the command is shown as a tooltip (an ANSI clear screen sequence can reset the contents, bash: <code>printf &#39;\e[2J&#39;</code>, C: <code>printf(&quot;\x1b[2J&quot;);</code>). If the standard error is empty, the tooltip will show information about the time when the command was last executed. <em>(since 0.12.4)</em></p></li>
<li><p><code>execp_font = [FAMILY-LIST] [STYLE-OPTIONS] [SIZE]</code> : The font used to draw the text. <em>(since 0.12.4)</em></p></li>
<li><p><code>execp_font_color = color opacity</code> : The font color. <em>(since 0.12.4)</em></p></li>
<li><p><code>execp_markup = boolean (0 or 1)</code> : If non-zero, the output of the command is treated as Pango markup, which allows rich text formatting. The format is <a href="https://developer.gnome.org/pygtk/stable/pango-markup-language.html">documented here</a>. Note that using this with commands that print data downloaded from the Internet is a possible security risk. <em>(since 0.12.4)</em></p></li>
@@ -683,6 +701,7 @@ execp_interval = 1
It is based on ttm, originally written by Pål Staurland <a href="mailto:staura@gmail.com">staura@gmail.com</a>.</p><p>This manual page was originally written by Daniel Moerner <a href="mailto:dmoerner@gmail.com">dmoerner@gmail.com</a>, for the Debian project (but may be used by others).
It was adopted from the tint2 docs.</p><h2 id="see-also">SEE ALSO<a name="see-also" href="#see-also" class="md2man-permalink" title="permalink"></a></h2><p>The main website <a href="https://gitlab.com/o9000/tint2">https://gitlab.com/o9000/tint2</a>
and the wiki page at <a href="https://gitlab.com/o9000/tint2/wikis/home">https://gitlab.com/o9000/tint2/wikis/home</a>.</p><p>This documentation is also provided in HTML and Markdown format in the system&#39;s default location
for documentation files, usually <code>/usr/share/doc/tint2</code> or <code>/usr/local/share/doc/tint2</code>.</p>
for documentation files, usually <code>/usr/share/doc/tint2</code> or <code>/usr/local/share/doc/tint2</code>.
.</p>
</body>
</html>

View File

@@ -199,9 +199,9 @@ pre {
</style>
</head>
<body>
<h1 id="latest-stable-release-0-14-1"><span class="md2man-title">Latest</span> <span class="md2man-section">stable</span> <span class="md2man-date">release:</span> <span class="md2man-source">0.14.1</span><a name="latest-stable-release-0-14-1" href="#latest-stable-release-0-14-1" class="md2man-permalink" title="permalink"></a></h1><p>Changes: <a href="https://gitlab.com/o9000/tint2/blob/0.14.1/ChangeLog">https://gitlab.com/o9000/tint2/blob/0.14.1/ChangeLog</a></p><p>Documentation: <a href="manual.html">manual.html</a></p><p>Compile it with (after you install the <a href="https://gitlab.com/o9000/tint2/wikis/Install#dependencies">dependencies</a>):</p><pre class="highlight plaintext"><code>git clone https://gitlab.com/o9000/tint2.git
<h1 id="latest-stable-release-16-6-1"><span class="md2man-title">Latest</span> <span class="md2man-section">stable</span> <span class="md2man-date">release:</span> <span class="md2man-source">16.6.1</span><a name="latest-stable-release-16-6-1" href="#latest-stable-release-16-6-1" class="md2man-permalink" title="permalink"></a></h1><p>Changes: <a href="https://gitlab.com/o9000/tint2/blob/16.6.1/ChangeLog">https://gitlab.com/o9000/tint2/blob/16.6.1/ChangeLog</a></p><p>Documentation: <a href="manual.html">manual.html</a></p><p>Compile it with (after you install the <a href="https://gitlab.com/o9000/tint2/wikis/Install#dependencies">dependencies</a>):</p><pre class="highlight plaintext"><code>git clone https://gitlab.com/o9000/tint2.git
cd tint2
git checkout 0.14.1
git checkout 16.6.1
mkdir build
cd build
cmake ..
@@ -239,9 +239,9 @@ update-mime-database /usr/local/share/mime
</ul>
<h1 id="known-issues">Known issues<a name="known-issues" href="#known-issues" class="md2man-permalink" title="permalink"></a></h1>
<ul>
<li>Graphic glitches on Intel graphics cards can be avoided by changing the acceleration method to UXA (<a href="https://gitlab.com/o9000/tint2/issues/595">issue 595</a>)</li>
<li>Window managers that do not follow exactly the EWMH specification might not interact well with tint2 (known issues for <a href="https://gitlab.com/o9000/tint2/issues/385">awesome</a>, <a href="https://gitlab.com/o9000/tint2/issues/524">bspwm</a>. <a href="https://gitlab.com/o9000/tint2/issues/456">openbox-multihead</a>)</li>
<li>Full transparency requires a compositor such as Compton (if not provided already by the window manager, as in Compiz/Unity, KDE or XFCE)</li>
<li>Graphical glitches on Intel graphics cards can be avoided by changing the acceleration method to UXA (<a href="https://gitlab.com/o9000/tint2/issues/595">issue 595</a>)</li>
<li>Window managers that do not follow exactly the EWMH specification might not interact well with tint2 (<a href="https://gitlab.com/o9000/tint2/issues/627">issue 627</a>).</li>
<li>Full transparency requires a compositor such as Compton (if not provided already by the window manager, as in Compiz/Unity, KDE or XFCE).</li>
</ul>
<h1 id="how-can-i-help-out">How can I help out?<a name="how-can-i-help-out" href="#how-can-i-help-out" class="md2man-permalink" title="permalink"></a></h1>
<ul>
@@ -256,12 +256,20 @@ update-mime-database /usr/local/share/mime
<li>Downloads: <a href="https://gitlab.com/o9000/tint2-archive/tree/master">https://gitlab.com/o9000/tint2-archive/tree/master</a> or <a href="https://code.google.com/p/tint2/downloads/list">https://code.google.com/p/tint2/downloads/list</a></li>
<li>Old project location (inactive): <a href="https://code.google.com/p/tint2">https://code.google.com/p/tint2</a></li>
</ul>
<h1 id="screenshots">Screenshots<a name="screenshots" href="#screenshots" class="md2man-permalink" title="permalink"></a></h1><h2 id="default-config">Default config:<a name="default-config" href="#default-config" class="md2man-permalink" title="permalink"></a></h2><p><img src="https://gitlab.com/o9000/tint2/uploads/948fa74eca60864352a033580350b4c3/Screenshot_2016-01-23_14-42-57.png" alt="Screenshot_2016-01-23_14-42-57"></p><h2 id="various-configs">Various configs:<a name="various-configs" href="#various-configs" class="md2man-permalink" title="permalink"></a></h2><p><img src="https://gitlab.com/o9000/tint2/wikis/screenshot.png" alt="screenshot"></p><h2 id="demos">Demos<a name="demos" href="#demos" class="md2man-permalink" title="permalink"></a></h2>
<h1 id="screenshots">Screenshots<a name="screenshots" href="#screenshots" class="md2man-permalink" title="permalink"></a></h1><h2 id="default-config">Default config:<a name="default-config" href="#default-config" class="md2man-permalink" title="permalink"></a></h2><p><img src="https://gitlab.com/o9000/tint2/uploads/948fa74eca60864352a033580350b4c3/Screenshot_2016-01-23_14-42-57.png" alt="Screenshot_2016-01-23_14-42-57"></p><h2 id="various-configs">Various configs:<a name="various-configs" href="#various-configs" class="md2man-permalink" title="permalink"></a></h2>
<ul>
<li><a href="https://gitlab.com/o9000/tint2/wikis/screenshots">Screenshots</a></li>
</ul>
<h2 id="demos">Demos<a name="demos" href="#demos" class="md2man-permalink" title="permalink"></a></h2>
<ul>
<li><a href="https://gitlab.com/o9000/tint2/wikis/whats-new-0.13.0.gif">Compact panel, separator, color gradients</a></li>
<li><a href="https://gitlab.com/o9000/tint2/wikis/whats-new-0.12.4.gif">Executor</a></li>
<li><a href="https://gitlab.com/o9000/tint2/wikis/whats-new-0.12.3.gif">Mouse over effects</a></li>
<li><a href="https://gitlab.com/o9000/tint2/wikis/whats-new-0.12.gif">Distribute size between taskbars, freespace</a></li>
</ul>
<h2 id="more">More<a name="more" href="#more" class="md2man-permalink" title="permalink"></a></h2>
<ul>
<li><a href="https://gitlab.com/o9000/tint2/wikis/Home">Tint2 wiki</a></li>
</ul>
</body>
</html>

View File

@@ -1,4 +1,4 @@
.TH TINT2 1 "2017\-04\-23" 0.14.2
.TH TINT2 1 "2018\-08\-05" 16.6.1
.SH NAME
.PP
tint2 \- lightweight panel/taskbar
@@ -150,6 +150,10 @@ The tint2 config file starts with the options defining background elements with
.IP \(bu 2
\fB\fCopacity\fR varies from (0 to 100), where 0 is fully transparent, 100 is fully opaque
.RE
.IP \(bu 2
\fB\fCborder_content_tint_weight = integer\fR : Mixes the border color with the content color (for tasks, this is the average color of the window icon). Values must be between 0 (no mixing) and 100 (fully replaces the color). \fI(since 16.0)\fP
.IP \(bu 2
\fB\fCbackground_content_tint_weight = integer\fR : Mixes the background color with the content color (for tasks, this is the average color of the window icon). Values must be between 0 (no mixing) and 100 (fully replaces the color). \fI(since 16.0)\fP
.RE
.PP
You can define as many backgrounds as you want. For example, the following config defines two backgrounds:
@@ -309,7 +313,7 @@ gradient_id_pressed = 2
.PP
For example, \fB\fCpanel_items = STC\fR will show the systray, the taskbar and the clock (from left to right).
.IP \(bu 2
\fB\fCpanel_monitor = monitor (all or 1 or 2 or ...)\fR : Which monitor tint2 draws the panel on
\fB\fCpanel_monitor = monitor (all or primary or 1 or 2 or ...)\fR : Which monitor tint2 draws the panel on
.RS
.IP \(bu 2
The first monitor is \fB\fC1\fR
@@ -317,7 +321,7 @@ The first monitor is \fB\fC1\fR
Use \fB\fCpanel_monitor = all\fR to get a separate panel per monitor
.RE
.IP \(bu 2
\fB\fCprimary_monitor_first = boolean (0 or 1)\fR : Place the primary monitor before all the other monitors in the list. \fI(since 0.12.4)\fP
\fB\fCprimary_monitor_first = boolean (0 or 1)\fR : Place the primary monitor before all the other monitors in the list. \fI(since 0.12.4; removed in 1.0, use \fB\fCprimary\fR instead)\fP
.RE
.PP
[](images/panel_padding.jpg)
@@ -339,6 +343,10 @@ Use \fB\fCpanel_monitor = all\fR to get a separate panel per monitor
\fB\fCwidth\fR and \fB\fCheight\fR can be specified without units (e.g. \fB\fC123\fR) as pixels, or followed by \fB\fC%\fR as percentages of the monitor size (e.g. \fB\fC50%\fR). Use \fB\fC100%\fR for full monitor width/height.
Example:
.RE
.IP \(bu 2
\fB\fCscale_relative_to_dpi = integer\fR : If set to a non\-zero value, HiDPI scaling is enabled. Each panel is visible on a different monitor. Thus each panel has a specific scaling factor. The scaling factor is computed as the ratio between the monitor DPI (obtained from the dimensions in pixels and millimeters from RandR) and a configured reference DPI \- this is the DPI for which exising user configs looked normal, for backward compatibility.
.IP \(bu 2
\fB\fCscale_relative_to_screen_height = integer\fR : Similar to \fB\fCscale_relative_to_dpi\fR, except the scaling factor is computed as the ratio between the monitor height and \fB\fCscale_relative_to_screen_height\fR\&. The effect is cumulative with \fB\fCscale_relative_to_dpi\fR, i.e. if both options are present, the factors are multiplied.
.RE
.PP
.RS
@@ -461,6 +469,8 @@ You can switch between virtual desktops.
.IP \(bu 2
\fB\fCtaskbar_hide_different_monitor = boolean (0 or 1)\fR : If enabled, the taskbar shows only the tasks from the current monitor. Useful when running different tint2 instances on different monitors, each one having its own config. \fI(since 0.12)\fP
.IP \(bu 2
\fB\fCtaskbar_hide_different_desktop = boolean (0 or 1)\fR : If enabled, the taskbar shows only the tasks from the current desktop. Useful to make multi\-desktop taskbars more compact, but still allow desktop switching with mouse click. \fI(since 1.0)\fP
.IP \(bu 2
\fB\fCtaskbar_always_show_all_desktop_tasks = boolean (0 or 1)\fR : Has effect only if \fB\fCtaskbar_mode = multi_desktop\fR\&. If enabled, tasks that appear on all desktops are shown on all taskbars. Otherwise, they are shown only on the taskbar of the current desktop. \fI(since 0.12.4)\fP
.IP \(bu 2
\fB\fCtaskbar_sort_order = none/title/center\fR : Specifies the sort order of the tasks on the taskbar. \fI(since 0.12)\fP
@@ -470,6 +480,8 @@ You can switch between virtual desktops.
.IP \(bu 2
\fB\fCtitle\fR : Sorts the tasks by title.
.IP \(bu 2
\fB\fCapplication\fR : Sorts the tasks by application name. \fI(since 16.3)\fP
.IP \(bu 2
\fB\fCcenter\fR : Sorts the tasks by their window centers.
.IP \(bu 2
\fB\fCmru\fR : Shows the most recently used tasks first. \fI(since 0.12.4)\fP
@@ -506,6 +518,10 @@ The following options configure the task buttons in the taskbar:
.IP \(bu 2
\fB\fCtask_tooltip = boolean (0 or 1)\fR : Whether to show tooltips for tasks.
.IP \(bu 2
\fB\fCtask_thumbnail = boolean (0 or 1)\fR : Whether to show thumbnail tooltips for tasks. \fI(since 16.0)\fP
.IP \(bu 2
\fB\fCtask_thumbnail_size = width\fR : Thumbnail size. \fI(since 16.0)\fP
.IP \(bu 2
\fB\fCtask_maximum_size = width height\fR
.RS
.IP \(bu 2
@@ -578,7 +594,7 @@ The action semantics:
.IP \(bu 2
\fB\fCsystray_icon_asb = alpha (0 to 100) saturation (\-100 to 100) brightness (\-100 to 100)\fR : Adjust the systray icons color and transparency.
.IP \(bu 2
\fB\fCsystray_monitor = integer (1, 2, ...)\fR : On which monitor to draw the systray. The first monitor is \fB\fC1\fR\&. \fI(since 0.12)\fP
\fB\fCsystray_monitor = integer (1, 2, ...) or primary\fR : On which monitor to draw the systray. The first monitor is \fB\fC1\fR\&. \fI(since 0.12)\fP
.IP \(bu 2
\fB\fCsystray_name_filter = string\fR : Regular expression to identify icon names to be hidden. For example, \fB\fC^audacious$\fR will hide icons with the exact name \fB\fCaudacious\fR, while \fB\fCaud\fR will hide any icons having \fB\fCaud\fR in the name. \fI(since 0.13.1)\fP
.RE
@@ -649,7 +665,9 @@ To hide the clock, comment \fB\fCtime1_format\fR and \fB\fCtime2_format\fR\&.
.IP \(bu 2
\fB\fCbattery_low_status = integer\fR: At what battery percentage the low command is executed.
.IP \(bu 2
\fB\fCbattery_low_cmd = notify\-send "battery low"\fR : Command to execute when the battery is low.
\fB\fCbattery_low_cmd = xmessage 'tint2: Battery low!'\fR : Command to execute when the battery is low.
.IP \(bu 2
\fB\fCbattery_full_cmd = notify\-send "battery full"\fR : Command to execute when the battery is full.
.IP \(bu 2
\fB\fCbat1_font = [FAMILY\-LIST] [STYLE\-OPTIONS] [SIZE]\fR
.IP \(bu 2
@@ -657,6 +675,22 @@ To hide the clock, comment \fB\fCtime1_format\fR and \fB\fCtime2_format\fR\&.
.IP \(bu 2
\fB\fCbattery_font_color = color opacity (0 to 100)\fR
.IP \(bu 2
\fB\fCbat1_format = FORMAT_STRING\fR : Format for battery line 1. Default: %p. \fI(since 1.0)\fP Format specification:
.RS
.IP \(bu 2
%s: State (charging, discharging, full, unknown).
.IP \(bu 2
%m: Minutes left until completely charged/discharged (estimated).
.IP \(bu 2
%h: Hours left until completely charged/discharged (estimated).
.IP \(bu 2
%t: Time left. Shows "hrs:mins" when charging/discharging, or "Ful\[rs]" when full.
.IP \(bu 2
%p: Percentage. Includes the % sign.
.RE
.IP \(bu 2
\fB\fCbat2_format = FORMAT_STRING\fR : Format for battery line 2. Default: %t. \fI(since 1.0)\fP
.IP \(bu 2
\fB\fCbattery_padding = horizontal_padding vertical_padding\fR
.IP \(bu 2
\fB\fCbattery_background_id = integer\fR : Which background to use for the battery.
@@ -696,7 +730,7 @@ To hide the clock, comment \fB\fCtime1_format\fR and \fB\fCtime2_format\fR\&.
.IP \(bu 2
\fB\fCexecp_icon_h = integer\fR : See \fB\fCexecp_icon_w\fR\&. \fI(since 0.12.4)\fP
.IP \(bu 2
\fB\fCexecp_tooltip = text\fR : The tooltip. Leave it empty to not display a tooltip. Not specifying this option leads to showing an automatically generated tooltip with information about when the command was last executed. \fI(since 0.12.4)\fP
\fB\fCexecp_tooltip = text\fR : The tooltip. If left empty, no tooltip is displayed. If missing, the standard error of the command is shown as a tooltip (an ANSI clear screen sequence can reset the contents, bash: \fB\fCprintf '\\e[2J'\fR, C: \fB\fCprintf("\\x1b[2J");\fR). If the standard error is empty, the tooltip will show information about the time when the command was last executed. \fI(since 0.12.4)\fP
.IP \(bu 2
\fB\fCexecp_font = [FAMILY\-LIST] [STYLE\-OPTIONS] [SIZE]\fR : The font used to draw the text. \fI(since 0.12.4)\fP
.IP \(bu 2
@@ -875,3 +909,4 @@ and the wiki page at \[la]https://gitlab.com/o9000/tint2/wikis/home\[ra]\&.
.PP
This documentation is also provided in HTML and Markdown format in the system's default location
for documentation files, usually \fB\fC/usr/share/doc/tint2\fR or \fB\fC/usr/local/share/doc/tint2\fR\&.
\&.

View File

@@ -1,4 +1,4 @@
# TINT2 1 "2017-04-23" 0.14.2
# TINT2 1 "2018-08-05" 16.6.1
## NAME
tint2 - lightweight panel/taskbar
@@ -119,6 +119,10 @@ The tint2 config file starts with the options defining background elements with
* `color` is specified in hex RGB, e.g. #ff0000 is red
* `opacity` varies from (0 to 100), where 0 is fully transparent, 100 is fully opaque
* `border_content_tint_weight = integer` : Mixes the border color with the content color (for tasks, this is the average color of the window icon). Values must be between 0 (no mixing) and 100 (fully replaces the color). *(since 16.0)*
* `background_content_tint_weight = integer` : Mixes the background color with the content color (for tasks, this is the average color of the window icon). Values must be between 0 (no mixing) and 100 (fully replaces the color). *(since 16.0)*
You can define as many backgrounds as you want. For example, the following config defines two backgrounds:
```
@@ -258,11 +262,11 @@ gradient_id_pressed = 2
For example, `panel_items = STC` will show the systray, the taskbar and the clock (from left to right).
* `panel_monitor = monitor (all or 1 or 2 or ...)` : Which monitor tint2 draws the panel on
* `panel_monitor = monitor (all or primary or 1 or 2 or ...)` : Which monitor tint2 draws the panel on
* The first monitor is `1`
* Use `panel_monitor = all` to get a separate panel per monitor
* `primary_monitor_first = boolean (0 or 1)` : Place the primary monitor before all the other monitors in the list. *(since 0.12.4)*
* `primary_monitor_first = boolean (0 or 1)` : Place the primary monitor before all the other monitors in the list. *(since 0.12.4; removed in 1.0, use `primary` instead)*
![](images/panel_padding.jpg)
@@ -275,6 +279,10 @@ gradient_id_pressed = 2
* `width` and `height` can be specified without units (e.g. `123`) as pixels, or followed by `%` as percentages of the monitor size (e.g. `50%`). Use `100%` for full monitor width/height.
Example:
* `scale_relative_to_dpi = integer` : If set to a non-zero value, HiDPI scaling is enabled. Each panel is visible on a different monitor. Thus each panel has a specific scaling factor. The scaling factor is computed as the ratio between the monitor DPI (obtained from the dimensions in pixels and millimeters from RandR) and a configured reference DPI - this is the DPI for which exising user configs looked normal, for backward compatibility.
* `scale_relative_to_screen_height = integer` : Similar to `scale_relative_to_dpi`, except the scaling factor is computed as the ratio between the monitor height and `scale_relative_to_screen_height`. The effect is cumulative with `scale_relative_to_dpi`, i.e. if both options are present, the factors are multiplied.
```
# The panel's width is 94% the size of the monitor, the height is 30 pixels:
panel_size = 94% 30
@@ -370,11 +378,14 @@ panel_size = 94% 30
* `taskbar_hide_different_monitor = boolean (0 or 1)` : If enabled, the taskbar shows only the tasks from the current monitor. Useful when running different tint2 instances on different monitors, each one having its own config. *(since 0.12)*
* `taskbar_hide_different_desktop = boolean (0 or 1)` : If enabled, the taskbar shows only the tasks from the current desktop. Useful to make multi-desktop taskbars more compact, but still allow desktop switching with mouse click. *(since 1.0)*
* `taskbar_always_show_all_desktop_tasks = boolean (0 or 1)` : Has effect only if `taskbar_mode = multi_desktop`. If enabled, tasks that appear on all desktops are shown on all taskbars. Otherwise, they are shown only on the taskbar of the current desktop. *(since 0.12.4)*
* `taskbar_sort_order = none/title/center` : Specifies the sort order of the tasks on the taskbar. *(since 0.12)*
* `none` : No sorting. New tasks are simply appended at the end of the taskbar when they appear.
* `title` : Sorts the tasks by title.
* `application` : Sorts the tasks by application name. *(since 16.3)*
* `center` : Sorts the tasks by their window centers.
* `mru` : Shows the most recently used tasks first. *(since 0.12.4)*
* `lru` : Shows the most recently used tasks last. *(since 0.12.4)*
@@ -407,6 +418,10 @@ The following options configure the task buttons in the taskbar:
* `task_tooltip = boolean (0 or 1)` : Whether to show tooltips for tasks.
* `task_thumbnail = boolean (0 or 1)` : Whether to show thumbnail tooltips for tasks. *(since 16.0)*
* `task_thumbnail_size = width` : Thumbnail size. *(since 16.0)*
* `task_maximum_size = width height`
* `width` is used with horizontal panels to limit the size of the tasks. Use `width = 0` to get full taskbar width.
* `height` is used with vertical panels.
@@ -471,7 +486,7 @@ The action semantics:
* `systray_icon_asb = alpha (0 to 100) saturation (-100 to 100) brightness (-100 to 100)` : Adjust the systray icons color and transparency.
* `systray_monitor = integer (1, 2, ...)` : On which monitor to draw the systray. The first monitor is `1`. *(since 0.12)*
* `systray_monitor = integer (1, 2, ...) or primary` : On which monitor to draw the systray. The first monitor is `1`. *(since 0.12)*
* `systray_name_filter = string` : Regular expression to identify icon names to be hidden. For example, `^audacious$` will hide icons with the exact name `audacious`, while `aud` will hide any icons having `aud` in the name. *(since 0.13.1)*
@@ -532,7 +547,9 @@ The action semantics:
* `battery_low_status = integer`: At what battery percentage the low command is executed.
* `battery_low_cmd = notify-send "battery low"` : Command to execute when the battery is low.
* `battery_low_cmd = xmessage 'tint2: Battery low!'` : Command to execute when the battery is low.
* `battery_full_cmd = notify-send "battery full"` : Command to execute when the battery is full.
* `bat1_font = [FAMILY-LIST] [STYLE-OPTIONS] [SIZE]`
@@ -540,6 +557,15 @@ The action semantics:
* `battery_font_color = color opacity (0 to 100)`
* `bat1_format = FORMAT_STRING` : Format for battery line 1. Default: %p. *(since 1.0)* Format specification:
* %s: State (charging, discharging, full, unknown).
* %m: Minutes left until completely charged/discharged (estimated).
* %h: Hours left until completely charged/discharged (estimated).
* %t: Time left. Shows "hrs:mins" when charging/discharging, or "Ful\" when full.
* %p: Percentage. Includes the % sign.
* `bat2_format = FORMAT_STRING` : Format for battery line 2. Default: %t. *(since 1.0)*
* `battery_padding = horizontal_padding vertical_padding`
* `battery_background_id = integer` : Which background to use for the battery.
@@ -578,7 +604,7 @@ The action semantics:
* `execp_icon_h = integer` : See `execp_icon_w`. *(since 0.12.4)*
* `execp_tooltip = text` : The tooltip. Leave it empty to not display a tooltip. Not specifying this option leads to showing an automatically generated tooltip with information about when the command was last executed. *(since 0.12.4)*
* `execp_tooltip = text` : The tooltip. If left empty, no tooltip is displayed. If missing, the standard error of the command is shown as a tooltip (an ANSI clear screen sequence can reset the contents, bash: `printf '\e[2J'`, C: `printf("\x1b[2J");`). If the standard error is empty, the tooltip will show information about the time when the command was last executed. *(since 0.12.4)*
* `execp_font = [FAMILY-LIST] [STYLE-OPTIONS] [SIZE]` : The font used to draw the text. *(since 0.12.4)*
@@ -739,3 +765,4 @@ and the wiki page at https://gitlab.com/o9000/tint2/wikis/home.
This documentation is also provided in HTML and Markdown format in the system's default location
for documentation files, usually `/usr/share/doc/tint2` or `/usr/local/share/doc/tint2`.
.

View File

@@ -1,9 +1,13 @@
#!/bin/sh
MAJOR=0.14
SCRIPT_DIR=$(dirname "$0")
DIRTY=""
VERSION=""
if git status 1>/dev/null 2>/dev/null
OLD_DIR=$(pwd)
cd ${SCRIPT_DIR}
if [ -d .git ] && git status 1>/dev/null 2>/dev/null
then
git update-index -q --ignore-submodules --refresh
# Disallow unstaged changes in the working tree
@@ -31,15 +35,31 @@ then
DIRTY="-dirty"
fi
fi
VERSION=$(git describe --exact-match 2>/dev/null || echo "$MAJOR-git$(git show -s --pretty=format:%ci | cut -d ' ' -f 1 | tr -d '-').$(git show -s --pretty=format:%h)")$DIRTY
else
VERSION=$(head -n 1 ChangeLog || head -n 1 ../ChangeLog | cut -d ' ' -f 2)
if [ $VERSION = "master" ]
if git describe 1>/dev/null 2>/dev/null
then
VERSION=$VERSION-$(head -n 1 ChangeLog || head -n 1 ../ChangeLog | cut -d ' ' -f 1)
VERSION=$(git describe 2>/dev/null)$DIRTY
elif git log -n 1 1>/dev/null 2>/dev/null
then
VERSION=$(head -n 1 "ChangeLog" | cut -d ' ' -f 2)
if [ "$VERSION" = "master" ]
then
PREVIOUS=$(grep '^2' "ChangeLog" | head -n 2 | tail -n 1 | cut -d ' ' -f 2)
HASH=$(git log -n 1 --pretty=format:%cI.%ct.%h | tr -d ':' | tr -d '-' | tr '.' '-' | sed 's/T[0-9\+]*//g' 2>/dev/null)
VERSION=$PREVIOUS-next-$HASH
fi
fi
fi
if [ -z "$VERSION" ]
then
VERSION=$(head -n 1 "ChangeLog" | cut -d ' ' -f 2)
if [ "$VERSION" = "master" ]
then
VERSION=$VERSION-$(head -n 1 "ChangeLog" | cut -d ' ' -f 1)
fi
fi
cd "${OLD_DIR}"
VERSION=$(echo "$VERSION" | sed 's/^v//')

View File

@@ -76,37 +76,25 @@ def get_last_version():
return tags[-1]
def inc_version(v, major=False, minor=False, rc=False):
if "-rc" in v:
# v4.0-rc7 -> v4.0-rc8 or v4.0
if minor:
return v.split("-rc")[0]
else:
parts = v.split("-rc")
parts[-1] = str(int(parts[-1]) + 1)
return "-rc".join(parts)
def inc_version(v, feature=False, tiny=False):
if v.startswith("v0."):
assert v == "v0.14.6"
return "v15.0"
# v4.11 -> v4.12 or v5.0 or v4.11.1
parts = v.split(".")
while len(parts) < 3:
parts.append("0")
assert len(parts) == 3
if feature:
del parts[-1]
parts[-2] = "v" + str(int(parts[-2].replace("v", "")) + 1)
parts[-1] = "0"
elif tiny:
parts[-1] = str(int(parts[-1]) + 1)
else:
# v4.11 = v4, 11, 0 -> v4.11.1 or v4.12 or v.4.12-rc1 or v5.0 or v5.0-rc1
# v4.11.7 = v4, 11, 7 -> ...
parts = v.split(".")
while len(parts) < 3:
parts.append("0")
assert len(parts) == 3
if major:
parts[-3] = "v" + str(int(parts[-3].replace("v", "")) + 1)
parts[-2] = "0"
if rc:
parts[-2] += "-rc1"
parts[-1] = ""
elif minor or rc:
parts[-2] = str(int(parts[-2]) + 1)
if rc:
parts[-2] += "-rc1"
parts[-1] = ""
else:
parts[-1] = str(int(parts[-1]) + 1)
assert not rc
return ".".join([s for s in parts if s])
del parts[-1]
parts[-1] = str(int(parts[-1]) + 1)
return ".".join([s for s in parts if s])
def assert_equal(a, b):
@@ -117,53 +105,23 @@ def assert_equal(a, b):
def test_inc_version():
# auto
assert_equal(inc_version("v1.0"), "v1.0.1")
assert_equal(inc_version("v1.0.1"), "v1.0.2")
assert_equal(inc_version("v1.0.2"), "v1.0.3")
assert_equal(inc_version("v1.0.10"), "v1.0.11")
assert_equal(inc_version("v1.1.10"), "v1.1.11")
assert_equal(inc_version("v1.1.10"), "v1.1.11")
# rc
assert_equal(inc_version("v1.0", False, False, True), "v1.1-rc1")
assert_equal(inc_version("v1.0.1", False, False, True), "v1.1-rc1")
assert_equal(inc_version("v1.0.2", False, False, True), "v1.1-rc1")
assert_equal(inc_version("v1.0.10", False, False, True), "v1.1-rc1")
assert_equal(inc_version("v1.1.10", False, False, True), "v1.2-rc1")
assert_equal(inc_version("v1.1.10", False, False, True), "v1.2-rc1")
# minor
assert_equal(inc_version("v1.0", False, True, False), "v1.1")
assert_equal(inc_version("v1.0.1", False, True, False), "v1.1")
assert_equal(inc_version("v1.0.2", False, True, False), "v1.1")
assert_equal(inc_version("v1.0.10", False, True, False), "v1.1")
assert_equal(inc_version("v1.1.10", False, True, False), "v1.2")
assert_equal(inc_version("v1.1.10", False, True, False), "v1.2")
# minor rc
assert_equal(inc_version("v1.0", False, True, True), "v1.1-rc1")
assert_equal(inc_version("v1.0.1", False, True, True), "v1.1-rc1")
assert_equal(inc_version("v1.0.2", False, True, True), "v1.1-rc1")
assert_equal(inc_version("v1.0.10", False, True, True), "v1.1-rc1")
assert_equal(inc_version("v1.1.10", False, True, True), "v1.2-rc1")
assert_equal(inc_version("v1.1.10", False, True, True), "v1.2-rc1")
# major rc
assert_equal(inc_version("v1.0", True, False, True), "v2.0-rc1")
assert_equal(inc_version("v1.0.1", True, False, True), "v2.0-rc1")
assert_equal(inc_version("v1.0.2", True, False, True), "v2.0-rc1")
assert_equal(inc_version("v1.0.10", True, False, True), "v2.0-rc1")
assert_equal(inc_version("v1.1.10", True, False, True), "v2.0-rc1")
assert_equal(inc_version("v1.1.10", True, False, True), "v2.0-rc1")
# major
assert_equal(inc_version("v1.0", True), "v2.0")
assert_equal(inc_version("v1.0.1", True), "v2.0")
assert_equal(inc_version("v1.0.2", True), "v2.0")
assert_equal(inc_version("v1.0.10", True), "v2.0")
assert_equal(inc_version("v1.1.10", True), "v2.0")
assert_equal(inc_version("v1.1.10", True), "v2.0")
# rc auto
assert_equal(inc_version("v1.0-rc1"), "v1.0-rc2")
assert_equal(inc_version("v1.1-rc2"), "v1.1-rc3")
# rc minor
assert_equal(inc_version("v1.0-rc1", False, True), "v1.0")
assert_equal(inc_version("v1.1-rc2", False, True), "v1.1")
assert_equal(inc_version("v0.14.6"), "v15.0")
assert_equal(inc_version("v15"), "v15.1")
assert_equal(inc_version("v15.0"), "v15.1")
assert_equal(inc_version("v16.1"), "v16.2")
assert_equal(inc_version("v16.10"), "v16.11")
# fix
assert_equal(inc_version("v0.14.6", False), "v15.0")
assert_equal(inc_version("v15", False), "v15.1")
assert_equal(inc_version("v15.0", False), "v15.1")
assert_equal(inc_version("v16.1", False), "v16.2")
assert_equal(inc_version("v16.10", False), "v16.11")
# feature
assert_equal(inc_version("v15", True), "v16.0")
assert_equal(inc_version("v15.0", True), "v16.0")
assert_equal(inc_version("v15.1", True), "v16.0")
assert_equal(inc_version("v15.2", True), "v16.0")
assert_equal(inc_version("v15.10", True), "v16.0")
def replace_in_file(path, before, after):
@@ -198,26 +156,15 @@ def update_log(path, version, date):
if __name__ == '__main__':
parser = argparse.ArgumentParser()
parser.add_argument("--major", action="store_true")
parser.add_argument("--minor", action="store_true")
parser.add_argument("--rc", action="store_true")
parser.add_argument("--undo", action="store_true")
parser.add_argument("--feature", action="store_true")
parser.add_argument("--tiny", action="store_true")
args = parser.parse_args()
logging.basicConfig(format=ansi_lblue + "%(asctime)s %(pathname)s %(levelname)s" + ansi_reset + " %(message)s", level=logging.DEBUG)
test_inc_version()
# Read version from last tag and increment
old_version = get_last_version()
if args.undo:
info("Revering last commit...")
run("git tag -d %s" % old_version)
run("git tag -d %s" % old_version.replace("v", ""))
run("git reset --soft HEAD~")
run("git reset")
run("git stash")
os.system("git log -1")
sys.exit(0)
info("Old version:", old_version)
version = inc_version(old_version, args.major, args.minor, args.rc)
version = inc_version(old_version, args.feature, args.tiny)
readable_version = version.replace("v", "")
date = datetime.datetime.now().strftime("%Y-%m-%d")
info("New version:", readable_version, version, date)
@@ -226,7 +173,7 @@ if __name__ == '__main__':
# Disallow uncommitted changes in the index
run("git diff-index --cached --quiet HEAD --ignore-submodules --")
# Update version string
replace_in_file("README.md", old_version, readable_version)
replace_in_file("README.md", old_version.replace("v", ""), readable_version)
update_man("doc/tint2.md", readable_version, date)
update_log("ChangeLog", readable_version, date)
run("git commit -am 'Release %s'" % readable_version)
@@ -237,4 +184,14 @@ if __name__ == '__main__':
run("tar -xzf tint2-%s.tar.gz" % readable_version)
run("cd tint2-%s ; mkdir build ; cd build ; cmake .. ; make" % readable_version)
assert_equal(run("./tint2-%s/build/tint2 -v" % readable_version).strip(), "tint2 version %s" % readable_version)
os.system("git log -p -1")
os.system("git log -p -1 --word-diff")
print "Does this look correct? [y/n]"
choice = raw_input().lower()
if choice != "y":
run("git reset --hard HEAD~ ; git tag -d %s ; git tag -d %s" % (version, readable_version))
sys.exit(1)
print "Publish? [y/n]"
choice = raw_input().lower()
if choice != "y":
sys.exit(1)
run("git push origin master && git push --tags origin master")

543
packaging/configure vendored Executable file
View File

@@ -0,0 +1,543 @@
#!/usr/bin/env python2.7
from __future__ import print_function
import argparse
import errno
import os
import shlex
import subprocess
import sys
import tempfile
def check_c_compiles(cmd, code):
with tempfile.NamedTemporaryFile(suffix='.c') as f:
f.write(code)
f.flush()
cmd += [f.name, '-o', '/dev/null']
proc = subprocess.Popen(
cmd,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE)
out, err = proc.communicate()
ret = proc.returncode
return ret == 0
def makedirs(path):
try:
os.makedirs(path)
except OSError as e:
if e.errno == errno.EEXIST and os.path.isdir(path):
pass
else:
raise
def pkg_config(lib):
def _pkgconfig(lib, query):
lib = lib.replace('>=', ' >= ')
cmd = 'pkg-config {} {}'.format(query, lib).split()
popen = subprocess.Popen(cmd,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE)
out, err = popen.communicate()
if popen.returncode != 0:
print(cmd)
print(err)
raise ValueError("Could not find library: {}".format(lib))
return out.split()
lf = _pkgconfig(lib, '--libs-only-l')
cf = _pkgconfig(lib, '--cflags')
ldf = _pkgconfig(lib, '--libs-only-L')
return (lf, cf, ldf)
def ninja_escape(s):
s = s.replace(' ', '$ ')
s = s.replace('\n', '$\n')
return s
def ninja_join(words):
indent = ' '
if len(words) > 1:
prefix = '$\n' + indent
else:
prefix = ''
spacer = ' $\n' + indent
return prefix + spacer.join(ninja_escape(s) for s in words)
def ninja_write_vars(f, **kwargs):
for k, v in kwargs.items():
f.write('{} = {}\n'.format(k, v))
f.write('\n')
def ninja_write_rule(f, name, **kwargs):
f.write('rule {}\n'.format(name))
for k, v in kwargs.items():
f.write(' {} = {}\n'.format(k, v))
f.write('\n')
def remove_prefix(s, prefix):
while prefix and s.startswith(prefix):
s = s[len(prefix):]
return s
def install(prefix, suffix, dest_dir, dest_suffix=''):
src_path = os.path.join(prefix, suffix) if suffix else prefix
fname = os.path.basename(src_path)
if dest_suffix:
dest_dir = os.path.join(dest_dir, dest_suffix)
dst_path = os.path.join(dest_dir, fname)
return [(src_path, dst_path)]
def install_dir(prefix, suffix, dest_dir, dest_suffix=''):
result = []
src_path = os.path.join(prefix, suffix)
for root, dirs, files in os.walk(src_path):
for f in files:
fname = os.path.join(root, f)
fsuffix = remove_prefix(remove_prefix(fname, prefix), '/')
result += install(prefix, fsuffix, dest_dir, os.path.join(dest_suffix, os.path.dirname(fsuffix)))
return result
class Executable(object):
def __init__(self, name):
self.name = name
self.cc = 'cc'
self.cflags = []
self.lflags = []
self.libs = []
self.sources = []
self.pos = []
self.install_exec = []
self.install_data = []
def write_ninja(self, f):
f.write('# Executable: {}\n'.format(self.name))
ninja_write_vars(f, **{
'cc_' + self.name: self.cc,
'cflags_' + self.name: ninja_join(self.cflags),
'lflags_' + self.name: ninja_join(self.lflags),
'libs_' + self.name: ninja_join(self.libs)})
ninja_write_rule(f, 'cc_' + self.name,
command='$cc_{} -MMD -MT $out -MF $out.d $cflags_{} -c $in -o $out'.format(self.name, self.name),
description='CC $out',
depfile='$out.d',
deps='gcc')
ninja_write_rule(f, 'link_' + self.name,
command='$cc_{} $lflags_{} -o $out $in $libs_{}'.format(self.name, self.name, self.name),
description='LINK $out')
ninja_write_rule(f, 'po2mo_' + self.name,
command='msgfmt -o $out $in',
description='GEN $out')
f.write('# Compilation\n')
for src in self.sources:
f.write('build $build_dir/{}.{}.o: cc_{} $source_dir/{}\n'.format(src, self.name, self.name, src))
f.write('# Translation\n')
for po in self.pos:
f.write('build $build_dir/{}.mo: po2mo_{} $source_dir/{}\n'.format(os.path.splitext(po)[0], self.name, po))
f.write('# Linking\n')
f.write('build $build_dir/{}: link_{} '.format(self.name, self.name))
f.write(ninja_join(['$build_dir/{}.{}.o'.format(src, self.name) for src in self.sources]))
f.write('\n')
f.write('# Installation\n')
for fin, fout in self.install_exec:
f.write('build {}: install_exec {}\n'.format(fout, fin))
for fin, fout in self.install_data:
f.write('build {}: install_data {}\n'.format(fout, fin))
f.write('build install_{}: phony '.format(self.name))
f.write(ninja_join([fout for fin, fout in self.install_exec + self.install_data]))
f.write('\n')
f.write('# Uninstallation\n')
for fin, fout in self.install_exec + self.install_data:
f.write('build uninstall_{}: uninstall {}\n'.format(fout, fout))
f.write('build uninstall_{}: phony '.format(self.name))
f.write(ninja_join(['uninstall_{}'.format(fout) for fin, fout in self.install_exec + self.install_data]))
f.write('\n')
f.write('\n')
def generate_ninja(targets, source_dir, build_dir):
ninja_file_name = os.path.join(build_dir, 'build.ninja')
f = open(ninja_file_name, 'w')
# `deps` was introduced in ninja 1.3.
ninja_write_vars(f, ninja_required_version='1.3')
ninja_write_vars(f, **{
'source_dir':source_dir,
'build_dir':build_dir
})
ninja_write_rule(f, 'install_exec',
command='install -D -m0755 $in $out',
description='INSTALL $out')
ninja_write_rule(f, 'install_data',
command='install -D -m0644 $in $out',
description='INSTALL $out')
ninja_write_rule(f, 'uninstall',
command='rm -f $in',
description='RM $in')
for t in targets:
t.write_ninja(f)
f.write('# Targets\n')
f.write('build all: phony ')
f.write(ninja_join(['$build_dir/{}'.format(t.name) for t in targets]))
f.write('\n')
installs = sum([(t.install_exec + t.install_data) for t in targets], [])
if installs:
f.write('build install: phony ')
f.write(ninja_join(['install_{}'.format(t.name) for t in targets]))
f.write('\n')
f.write('build uninstall: phony ')
f.write(ninja_join(['uninstall_{}'.format(t.name) for t in targets]))
f.write('\n')
f.write('\n')
f.write('default all\n')
f.close()
print('Wrote {}.'.format(ninja_file_name))
print('Run `ninja -v -C {} all` to compile.'.format(build_dir))
print('Run `ninja -v -C {} install` to install.'.format(build_dir))
# Parse CLI options
parser = argparse.ArgumentParser()
parser.add_argument('--uevent', dest='uevent', action='store_true',
help='Enable uevent support. Default: on under Linux')
parser.add_argument('--debug', dest='debug', action='store_true',
help='Enable debug build. Default: off')
parser.add_argument('--asan', dest='asan', action='store_true',
help='Enable AddressSanitizer. Default: off')
parser.add_argument('--tracing', dest='tracing', action='store_true',
help='Enable tracing. Default: off')
parser.add_argument('--memory-tracing', dest='memory_tracing', action='store_true',
help='Enable memory allocation tracing. Default: off')
parser.add_argument('--prefix', help='Prefix for constructing the file installation paths. Default: /usr/local', default=None)
parser.add_argument('--exec_prefix', help='Prefix for binary paths. Default: $prefix', default=None)
parser.add_argument('--bindir', help='Path where executables must be installed. Default: $exec_prefix/bin', default=None)
parser.add_argument('--sysconfdir', help='Path where config files must be installed. Default: /etc', default=None)
parser.add_argument('--datarootdir', help='Path where data files must be installed. Default: $prefix/share', default=None)
parser.add_argument('--localedir', help='Path where locale files must be installed. Default: $datarootdir/locale', default=None)
parser.add_argument('--docdir', help='Path where documentation files must be installed. Default: $datarootdir/doc/tint2', default=None)
parser.add_argument('--htmldir', help='Path where documentation files must be installed. Default: $docdir/html', default=None)
parser.add_argument('--mandir', help='Path where man files must be installed. Default: $datarootdir/man', default=None)
parser.add_argument('--home', dest='home', action='store_true',
help='Install to $HOME (sets all paths accordingly). Default: off')
args = parser.parse_args()
# Get relevant environment variables
CC = os.environ.get('CC', 'cc')
CFLAGS = shlex.split(os.environ.get('CFLAGS', ''))
LFLAGS = shlex.split(os.environ.get('LDFLAGS', ''))
LIBS = []
# Get paths
source_dir = os.path.realpath(os.path.dirname(os.path.realpath(__file__)) + '/..')
build_dir = os.path.join(os.getcwd(), 'build')
if not args.home:
prefix = args.prefix or '/usr/local'
exec_prefix = args.exec_prefix or prefix
bindir = args.bindir or os.path.join(exec_prefix, 'bin')
datarootdir = args.datarootdir or os.path.join(prefix, 'share')
sysconfdir = args.sysconfdir or '/etc'
docdir = args.docdir or os.path.join(datarootdir, 'doc/tint2')
htmldir = args.htmldir or os.path.join(docdir, 'html')
localedir = args.localedir or os.path.join(datarootdir, 'locale')
mandir = args.mandir or os.path.join(datarootdir, 'man')
else:
prefix = args.prefix or os.path.expanduser("~")
exec_prefix = args.exec_prefix or prefix
bindir = args.bindir or os.path.join(exec_prefix, 'bin')
datarootdir = args.datarootdir or os.path.join(prefix, '.local/share')
sysconfdir = args.sysconfdir or os.path.expanduser("~/.config/tint2")
docdir = args.docdir or os.path.join(datarootdir, 'doc/tint2')
htmldir = args.htmldir or os.path.join(docdir, 'html')
localedir = args.localedir or os.path.join(datarootdir, 'locale')
mandir = args.mandir or os.path.join(datarootdir, 'man')
# Check if C11 is supported by the compiler, fall back to C99
if check_c_compiles([CC],
'''#define print(x) _Generic((x), default : print_unknown)(x)
void print_unknown() {
}
int main () {
print(0);
}'''):
CFLAGS += ['-std=c11', '-DHAS_GENERIC']
else:
print("No C11 support.")
CFLAGS += ['-std=c99']
# Set mandatory flags
CFLAGS += ['-g',
'-Wall',
'-Wextra',
'-Wshadow',
'-Wpointer-arith',
'-Wno-deprecated',
'-Wno-missing-field-initializers',
'-Wno-unused-parameter',
'-Wno-sign-compare',
'-fno-strict-aliasing',
'-pthread',
'-D_BSD_SOURCE',
'-D_DEFAULT_SOURCE',
'-D_WITH_GETLINE',
'-DENABLE_BATTERY']
LFLAGS += ['-fno-strict-aliasing',
'-pthread']
LFLAGS += ['-L' + build_dir]
# Set platform dependent C flags
if sys.platform.startswith('linux'):
CFLAGS += ['-D_POSIX_C_SOURCE=200809L']
if sys.platform.startswith('freebsd') or sys.platform.startswith('openbsd') or sys.platform.startswith('dragonfly'):
CFLAGS += ['-I/usr/local/include']
LFLAGS += ['-L/usr/local/lib']
if sys.platform.startswith('linux') or args.uevent:
CFLAGS += ['-DENABLE_UEVENT']
# Turn on color messages if supported
if check_c_compiles([CC, '-fdiagnostics-color', '-c', '-x', 'c'], ''):
CFLAGS += ['-fdiagnostics-color=always']
# Set project-specific include dirs
CFLAGS += ['-I.']
for inc in ['src',
'src/battery',
'src/clock',
'src/systray',
'src/taskbar',
'src/launcher',
'src/tooltip',
'src/util',
'src/execplugin',
'src/button',
'src/freespace',
'src/separator']:
CFLAGS += ['-I' + os.path.join(source_dir, inc)]
# Add mandatory library dependencies
LIBS += ['-lm', '-lrt']
# Add mandatory libray dependencies detected with pkg-config
for dep in ['x11',
'xcomposite',
'xdamage',
'xinerama',
'xext',
'xrender',
'xrandr>=1.3',
'pangocairo',
'pango',
'cairo',
'glib-2.0',
'gobject-2.0',
'imlib2>=1.4.2']:
lib, cf, lf = pkg_config(dep)
LIBS += lib
CFLAGS += cf
LFLAGS += lf
# Add optional library dependencies detected with pkg-config
try:
lib, cf, lf = pkg_config('librsvg-2.0>=2.14.0')
LIBS += lib
CFLAGS += cf + ['-DHAVE_RSVG']
LFLAGS += lf
except:
print("No SVG support.")
try:
lib, cf, lf = pkg_config('libstartup-notification-1.0>=0.12')
LIBS += lib
CFLAGS += cf + ['-DHAVE_SN', '-DSN_API_NOT_YET_FROZEN']
LFLAGS += lf
except:
print("No startup notification support.")
# Add library dependencies detected with using successful compilation test
bt = False
if not bt:
try:
lib, cf, lf = pkg_config('libunwind')
LIBS += lib
CFLAGS += cf + ['-DHAS_LIBUNWIND']
LFLAGS += lf
bt = True
print("Backtrace support via libunwind.")
except:
print("No backtrace support via libunwind.")
if not bt:
if check_c_compiles([CC, '-lbacktrace'], '''#include <backtrace.h>
int main() {
return 0;
}'''):
CFLAGS += ['-DHAS_BACKTRACE']
LIBS += ['-lbacktrace']
bt = True
print("Backtrace support via libbacktrace.")
else:
print("No backtrace support via libbacktrace.")
# Add option-dependent flags
if not args.debug:
CFLAGS += ['-O2']
if args.asan:
asan_flags = ['-fsanitize=address']
CFLAGS += asan_flags
LFLAGS += asan_flags
if args.tracing:
CFLAGS += ['-finstrument-functions',
'-finstrument-functions-exclude-file-list=tracing.c',
'-finstrument-functions-exclude-function-list=get_time,gettime']
if args.asan or args.memory_tracing or args.tracing:
trace_flags = ['-O0',
'-fno-common',
'-fno-omit-frame-pointer',
'-rdynamic']
CFLAGS += trace_flags
LFLAGS += trace_flags + ['-fuse-ld=gold']
if args.memory_tracing:
LIBS += ['-ldl']
# Define targets
tint2 = Executable('tint2')
tint2.cflags += CFLAGS
tint2.lflags += LFLAGS
tint2.libs += LIBS
tint2.sources = ['src/config.c',
'src/panel.c',
'src/util/server.c',
'src/main.c',
'src/init.c',
'src/util/signals.c',
'src/util/tracing.c',
'src/mouse_actions.c',
'src/drag_and_drop.c',
'src/default_icon.c',
'src/clock/clock.c',
'src/systray/systraybar.c',
'src/launcher/launcher.c',
'src/launcher/apps-common.c',
'src/launcher/icon-theme-common.c',
'src/launcher/xsettings-client.c',
'src/launcher/xsettings-common.c',
'src/taskbar/task.c',
'src/taskbar/taskbar.c',
'src/taskbar/taskbarname.c',
'src/tooltip/tooltip.c',
'src/execplugin/execplugin.c',
'src/button/button.c',
'src/freespace/freespace.c',
'src/separator/separator.c',
'src/tint2rc.c',
'src/util/area.c',
'src/util/bt.c',
'src/util/common.c',
'src/util/fps_distribution.c',
'src/util/strnatcmp.c',
'src/util/timer.c',
'src/util/cache.c',
'src/util/color.c',
'src/util/strlcat.c',
'src/util/print.c',
'src/util/gradient.c',
'src/util/test.c',
'src/util/uevent.c',
'src/util/window.c',
'src/battery/battery.c']
# Battery implementation is platform-specific
if sys.platform.startswith('linux'):
tint2.sources += ['src/battery/linux.c']
elif sys.platform.startswith('freebsd') or \
sys.platform.startswith('dragonfly') or \
sys.platform.startswith('gnukfreebsd'):
tint2.sources += ['src/battery/freebsd.c']
elif sys.platform.startswith('openbsd') or \
sys.platform.startswith('netbsd'):
tint2.sources += ['src/battery/openbsd.c']
else:
print("No battery support for platform:", sys.platform)
tint2.sources += ['src/battery/dummy.c']
if args.memory_tracing:
tint2.sources += ['src/util/mem.c']
tint2.install_exec = install(build_dir, 'tint2', bindir)
tint2.install_data = (install(source_dir, 'tint2.svg', datarootdir, 'icons/hicolor/scalable/apps') +
install(source_dir, 'tint2.desktop', datarootdir, 'applications') +
install(source_dir, 'themes/tint2rc', sysconfdir, 'xdg/tint2') +
install(source_dir, 'default_icon.png', datarootdir, 'tint2') +
install(source_dir, 'AUTHORS', docdir) +
install(source_dir, 'ChangeLog', docdir) +
install(source_dir, 'README.md', docdir) +
install(source_dir, 'doc/tint2.md', docdir) +
install(source_dir, 'doc/manual.html', htmldir) +
install(source_dir, 'doc/readme.html', htmldir) +
install_dir(source_dir, 'doc/images', htmldir) +
install(source_dir, 'doc/tint2.1', mandir, 'man1'))
tint2conf = Executable('tint2conf')
tint2conf.cflags += CFLAGS
tint2conf.lflags += LFLAGS
tint2conf.libs += LIBS
for dep in ['gthread-2.0',
'gtk+-x11-2.0']:
lib, cf, lf = pkg_config(dep)
tint2conf.libs += lib
tint2conf.cflags += cf
tint2conf.lflags += lf
tint2conf.cflags += ['-DTINT2CONF',
'-DINSTALL_PREFIX=\\"{}\\"'.format(prefix),
'-DLOCALEDIR=\\"{}\\"'.format(localedir),
'-DGETTEXT_PACKAGE=\\"tint2conf\\"',
'-DHAVE_VERSION_H']
tint2conf.sources = ['src/util/bt.c',
'src/util/common.c',
'src/util/strnatcmp.c',
'src/util/cache.c',
'src/util/timer.c',
'src/util/test.c',
'src/util/print.c',
'src/util/signals.c',
'src/config.c',
'src/util/server.c',
'src/util/strlcat.c',
'src/launcher/apps-common.c',
'src/launcher/icon-theme-common.c',
'src/tint2conf/md4.c',
'src/tint2conf/main.c',
'src/tint2conf/properties.c',
'src/tint2conf/properties_rw.c',
'src/tint2conf/theme_view.c',
'src/tint2conf/background_gui.c',
'src/tint2conf/gradient_gui.c']
tint2conf.pos = [os.path.join('src/tint2conf/po', f) for f in os.listdir('src/tint2conf/po') if f.endswith('.po')]
tint2conf.install_exec = install(build_dir, 'tint2conf', bindir)
tint2conf.install_data = (install(source_dir, 'src/tint2conf/tint2conf.svg', datarootdir, 'icons/hicolor/scalable/apps') +
install(source_dir, 'src/tint2conf/tint2conf.desktop', datarootdir, 'applications') +
install(source_dir, 'src/tint2conf/tint2conf.xml', datarootdir, 'mime/packages'))
makedirs(build_dir)
assert 0 == os.system('cd {}; {}/get_version.sh'.format(build_dir, source_dir))
generate_ninja([tint2, tint2conf], source_dir, build_dir)

View File

@@ -23,7 +23,8 @@ Homepage: https://gitlab.com/o9000/tint2/
Package: tint2
Architecture: any
Depends: ${shlibs:Depends},
${misc:Depends}
${misc:Depends},
procps
Description: lightweight taskbar
Tint is a simple panel/taskbar intentionally made for openbox3, but should
also work with other window managers. The taskbar includes transparency and

View File

@@ -0,0 +1,2 @@
pkill -SIGUSR2 tint2 || true

View File

@@ -4,7 +4,7 @@
dh $@
override_dh_auto_configure:
dh_auto_configure -- -DCMAKE_BUILD_TYPE=RelWithDebInfo
dh_auto_configure -- -DCMAKE_BUILD_TYPE=RelWithDebInfo -DCMAKE_INSTALL_SYSCONFDIR=/etc
override_dh_auto_install:
dh_auto_install --destdir=$(CURDIR)/debian/tmp

View File

@@ -21,7 +21,7 @@ then
exit 1
fi
rm -f version.h
VERSION=$(git describe --exact-match 2>/dev/null)
VERSION=$(false 2>/dev/null)
if [ $? -eq 0 ]
then
VERSION=$(echo "$VERSION" | sed 's/^v//')
@@ -49,7 +49,7 @@ echo "echo \"#define VERSION_STRING \\\"$VERSION\\\"\" > version.h" > $DIR/get_v
# Copy the debian files into the source directory
cp -r debian $DIR/debian
for DISTRO in precise trusty xenial yakkety zesty
for DISTRO in trusty xenial artful bionic
do
# Cleanup from previous builds
rm -rf tint2_$VERSION-*

View File

@@ -0,0 +1,23 @@
#!/bin/bash
export PATH=/usr/local/bin:/usr/bin:/bin:/usr/local/sbin:/usr/sbin:/sbin
set -e
set -x
[ "${FLOCKER}" != "$0" ] && exec env FLOCKER="$0" flock -en "$0" "$0" "$@" || :
exec > ~/tint2.runner-version.log
exec 2>&1
cd ~/tint2.wiki
git reset --hard
git pull
timeout -k 10 600 ~/tint2/packaging/version_status.py > packaging.tmp.md
cat packaging.tmp.md > packaging.md
rm packaging.tmp.md
git commit -am 'Update packaging info'
git push origin master

422
packaging/version_status.py Executable file
View File

@@ -0,0 +1,422 @@
#!/usr/bin/env python2
# -*- coding: utf-8 -*-
import sys
reload(sys)
sys.setdefaultencoding('utf8')
import datetime
import xml.etree.ElementTree as ET
import ftplib
import gzip
import json
import re
from StringIO import StringIO
import urllib2
# Returns true if `value` is an integer represented as a string.
def is_int(value):
# type: (str) -> bool
try:
value = int(value)
except ValueError:
return False
return True
# Returns a new string with all instances of multiple whitespace
# replaced with a single space.
def collapse_multiple_spaces(line):
# type: (str) -> str
return " ".join(line.split())
# Extracts the file name from a line of an FTP listing.
# The input must be a valid directory entry (starting with "-" or "d").
def ftp_file_name_from_listing(line):
# type: (str) -> str
line = collapse_multiple_spaces(line)
return line.split(" ", 8)[-1]
# Extracts a list of the directories and a list of the files
# from an FTP listing.
def ftp_list_dir_process_listing(lines):
# type: (List[str]) -> List[str], List[str]
dirs = []
files = []
for line in lines:
if line.startswith("d"):
dirs.append(ftp_file_name_from_listing(line))
elif line.startswith("-"):
files.append(ftp_file_name_from_listing(line))
return dirs, files
# Lists the remote FTP directory located at `path`.
# Returns a list of the directories and a list of the files.
def ftp_list_dir(ftp, path):
# type: (ftplib.FTP, str) -> List[str], List[str]
ftp.cwd(path)
lines = []
ftp.retrlines("LIST", lines.append)
return ftp_list_dir_process_listing(lines)
# Downloads a binary file to a string.
# Returns the string.
def ftp_download(ftp, path):
# type: (ftplib.FTP, str) -> str
blocks = []
ftp.retrbinary("RETR {0}".format(path), blocks.append)
return "".join(blocks)
# Extracts the list of links from an HTML string.
def http_links_from_listing(html):
# type: (str) -> List[str]
pattern = re.compile(r"""href=['"]+([^'"]+)['"]+""")
return re.findall(pattern, html)
# Extracts the list of paths (relative links, except to ../*) from an HTML string.
def http_paths_from_listing(html):
# type: (str) -> List[str]
paths = []
for link in http_links_from_listing(html):
if link.startswith(".."):
continue
if link == "./" or link == "/":
continue
if "://" in link:
continue
paths.append(link)
return paths
# Downloads a file as string from an URL. Decodes correctly.
def http_download_txt(url):
# type: (str) -> str
try:
r = urllib2.urlopen(url)
encoding = r.headers.getparam("charset")
if not encoding:
encoding = "utf-8"
return r.read().decode(encoding)
except:
raise
# Extracts the list of paths (relative links, except to ../*) from the HTML code
# located at `url`.
def http_list_dir(url):
# type: (str) -> List[str]
try:
html = http_download_txt(url)
except:
return []
return http_paths_from_listing(html)
# Extracts the version and maintainer info for a package, from a Debian repository Packages file.
def deb_packages_extract_version(packages, name):
# type: (str, str) -> str, str
inside = False
version = None
maintainer = None
for line in packages.split("\n"):
if line == "Package: " + name:
inside = True
elif not line:
if inside:
break
else:
if inside:
if line.startswith("Version:"):
version = line.split(":", 1)[-1].strip()
elif line.startswith("Maintainer:"):
maintainer = line.split(":", 1)[-1].strip()
return version, maintainer
# Extracts the version and maintainer info for a package, from an Arch PKGBUILD file.
def arch_pkgbuild_extract_version(pkgbuild):
# type: (str) -> str, str
version = None
maintainer = None
for line in pkgbuild.split("\n"):
if line.startswith("# Maintainer:"):
maintainer = line.split(":", 1)[-1].strip()
elif line.startswith("pkgver="):
version = line.split("=", 1)[-1].strip()
return version, maintainer
# Debian
def get_debian_release_version(release):
data = http_download_txt("http://metadata.ftp-master.debian.org/changelogs/main/t/tint2/{0}_changelog".format(release))
version = data.split("\n", 1)[0].split("(", 1)[-1].split(")", 1)[0].strip()
maintainer = [line.split("--", 1)[-1] for line in data.split("\n") if line.startswith(" --")][0].split(" ")[0].strip()
return release, version, maintainer
def get_debian_versions():
print >> sys.stderr, "Debian ..."
return "Debian", "debian", [get_debian_release_version(release) for release in ["stable", "testing", "unstable", "experimental"]]
# Ubuntu
def get_ubuntu_versions():
print >> sys.stderr, "Ubuntu ..."
data = http_download_txt("https://api.launchpad.net/1.0/ubuntu/+archive/primary?ws.op=getPublishedSources&source_name=tint2&exact_match=true")
data = json.loads(data)["entries"]
data.reverse()
versions = []
for package in data:
if package["status"] == "Published":
version = package["source_package_version"]
release = package["distro_series_link"].split("/")[-1]
maintainer = package["package_maintainer_link"]
versions.append((release, version, maintainer))
return "Ubuntu", "ubuntu", versions
# BunsenLabs
def get_bunsenlabs_versions():
print >> sys.stderr, "BunsenLabs ..."
dirs = http_list_dir("https://eu.pkg.bunsenlabs.org/debian/dists/")
versions = []
for d in dirs:
if d.endswith("/") and "/" not in d[:-1]:
release = d.replace("/", "")
packages = http_download_txt("https://eu.pkg.bunsenlabs.org/debian/dists/{0}/main/binary-amd64/Packages".format(release))
version, maintainer = deb_packages_extract_version(packages, "tint2")
if version:
versions.append((release, version, maintainer))
return "BunsenLabs", "bunsenlabs", versions
# Arch
def get_arch_versions():
print >> sys.stderr, "Arch ..."
pkgbuild = http_download_txt("https://git.archlinux.org/svntogit/community.git/plain/trunk/PKGBUILD?h=packages/tint2")
version, maintainer = arch_pkgbuild_extract_version(pkgbuild)
return "Arch Linux", "archlinux", [("Community", version, maintainer)]
# Fedora
def get_fedora_versions():
print >> sys.stderr, "Fedora ..."
dirs = http_list_dir("http://mirror.switch.ch/ftp/mirror/fedora/linux/development/")
versions = []
for d in dirs:
if d.endswith("/") and "/" not in d[:-1]:
release = d.replace("/", "")
packages = http_list_dir("http://mirror.switch.ch/ftp/mirror/fedora/linux/development/{0}/Everything/source/tree/Packages/t/".format(release))
for p in packages:
if p.startswith("tint2-"):
version = p.split("-", 1)[-1].split(".fc")[0]
v = (release, version, "")
if v not in versions:
versions.append(v)
return "Fedora", "fedora", versions
# Red Hat (EPEL)
def get_redhat_epel_versions():
print >> sys.stderr, "RedHat ..."
dirs = http_list_dir("http://mirror.switch.ch/ftp/mirror/epel/")
versions = []
for d in dirs:
if d.endswith("/") and "/" not in d[:-1] and is_int(d[:-1]):
release = d.replace("/", "")
packages = http_list_dir("http://mirror.switch.ch/ftp/mirror/epel/{0}/SRPMS/t/".format(release))
for p in packages:
if p.startswith("tint2-"):
version = p.split("-", 1)[-1].split(".el")[0]
v = (release, version, "")
if v not in versions:
versions.append(v)
return "RedHat (EPEL)", "rhel", versions
# SUSE
def get_suse_versions():
print >> sys.stderr, "Suse ..."
ftp = ftplib.FTP("mirror.switch.ch")
ftp.login()
releases, _ = ftp_list_dir(ftp, "/mirror/opensuse/opensuse/ports/update/leap/")
versions = []
for release in releases:
root = "/mirror/opensuse/opensuse/ports/update/leap/{0}/oss/repodata/".format(release)
_, files = ftp_list_dir(ftp, root)
for fname in files:
if fname.endswith("-primary.xml.gz"):
data = ftp_download(ftp, "{0}/{1}".format(root, fname))
xml = gzip.GzipFile(fileobj=StringIO(data)).read()
root = ET.fromstring(xml)
for package in root.findall("{http://linux.duke.edu/metadata/common}package"):
name = package.find("{http://linux.duke.edu/metadata/common}name").text
if name == "tint2":
version = package.find("{http://linux.duke.edu/metadata/common}version").get("ver")
versions.append((release, version, ""))
ftp.quit()
return "OpenSUSE", "opensuse", versions
# Gentoo
def get_gentoo_versions():
print >> sys.stderr, "Gentoo ..."
files = http_list_dir("https://gitweb.gentoo.org/repo/gentoo.git/tree/x11-misc/tint2")
versions = []
for f in files:
if "tint2" in f and f.endswith(".ebuild"):
version = f.split("tint2-")[-1].split(".ebuild")[0]
v = ("", version, "")
if v not in versions:
versions.append(v)
return "Gentoo", "gentoo", versions
# Void
def get_void_versions():
print >> sys.stderr, "Void ..."
template = http_download_txt("https://raw.githubusercontent.com/void-linux/void-packages/master/srcpkgs/tint2/template")
versions = []
version = None
maintainer = None
for line in template.split("\n"):
if line.startswith("version="):
version = line.split("=", 1)[-1].replace('"', "").strip()
elif line.startswith("maintainer="):
maintainer = line.split("=", 1)[-1].replace('"', "").strip()
if version:
versions.append(("", version, maintainer))
return "Void Linux", "void", versions
# Alpine
def get_alpine_versions():
print >> sys.stderr, "Alpine ..."
apkbuild = http_download_txt("https://git.alpinelinux.org/cgit/aports/plain/community/tint2/APKBUILD")
versions = []
version = None
maintainer = None
for line in apkbuild.split("\n"):
if line.startswith("pkgver="):
version = line.split("=", 1)[-1].replace('"', "").strip()
elif line.startswith("# Maintainer:"):
maintainer = line.split(":", 1)[-1].replace('"', "").strip()
if version:
versions.append(("", version, maintainer))
return "Alpine Linux", "alpine", versions
# Slackware
def get_slack_versions():
print >> sys.stderr, "Slackware ..."
dirs = http_list_dir("https://slackbuilds.org/slackbuilds/")
versions = []
for d in dirs:
if d.endswith("/") and "/" not in d[:-1]:
release = d.replace("/", "")
try:
info = http_download_txt("https://slackbuilds.org/slackbuilds/{0}/desktop/tint2/tint2.info".format(release))
except:
continue
version = None
maintainer = None
for line in info.split("\n"):
if line.startswith("VERSION="):
version = line.split("=", 1)[-1].replace('"', "").strip()
elif line.startswith("MAINTAINER="):
maintainer = line.split("=", 1)[-1].replace('"', "").strip()
if version:
versions.append((release, version, maintainer))
return "Slackware", "slackware", versions
# FreeBSD
def get_freebsd_versions():
print >> sys.stderr, "FreeBSD ..."
makefile = http_download_txt("https://svnweb.freebsd.org/ports/head/x11/tint/Makefile?view=co")
versions = []
version = None
maintainer = None
for line in makefile.split("\n"):
if line.startswith("PORTVERSION="):
version = line.split("=", 1)[-1].strip()
elif line.startswith("MAINTAINER="):
maintainer = line.split("=", 1)[-1].strip()
if version:
versions.append(("", version, maintainer))
return "FreeBSD", "freebsd", versions
# OpenBSD
def get_openbsd_versions():
print >> sys.stderr, "OpenBSD ..."
makefile = http_download_txt("http://cvsweb.openbsd.org/cgi-bin/cvsweb/~checkout~/ports/x11/tint2/Makefile?content-type=text/plain")
versions = []
version = None
for line in makefile.split("\n"):
if line.startswith("V="):
version = line.split("=", 1)[-1].strip()
if version:
versions.append(("", version, ""))
return "OpenBSD", "openbsd", versions
# Upstream
def get_tint2_version():
print >> sys.stderr, "Upstream ..."
readme = http_download_txt("https://gitlab.com/o9000/tint2/raw/master/README.md")
version = readme.split("\n", 1)[0].split(":", 1)[-1].strip()
return version
def main():
latest = get_tint2_version()
distros = []
distros.append(get_debian_versions())
distros.append(get_bunsenlabs_versions())
distros.append(get_ubuntu_versions())
distros.append(get_fedora_versions())
distros.append(get_redhat_epel_versions())
#distros.append(get_suse_versions())
distros.append(get_alpine_versions())
distros.append(get_slack_versions())
distros.append(get_arch_versions())
distros.append(get_void_versions())
distros.append(get_gentoo_versions())
distros.append(get_freebsd_versions())
distros.append(get_openbsd_versions())
print "| Distribution | Release | Version | Status |"
print "| ------------ | ------- | ------- | ------ |"
for dist, dcode, releases in distros:
icon = "![](numix-icons/distributor-logo-{0}.svg.png)".format(dcode)
for r in releases:
if r[1].split("-", 1)[0] == latest:
status = ":white_check_mark: Latest"
else:
status = ":warning: Out of date"
print "| {0} {1} | {2} | {3} | {4} |".format(icon, dist, r[0], r[1], status)
utc_datetime = datetime.datetime.utcnow()
print ""
print "Last updated:", utc_datetime.strftime("%Y-%m-%d %H:%M UTC")
if __name__ == "__main__":
main()

208
packaging/version_status_test.py Executable file
View File

@@ -0,0 +1,208 @@
#!/usr/bin/env python2
# -*- coding: utf-8 -*-
import unittest
from version_status import *
class TestStringFunctions(unittest.TestCase):
def test_collapse_multiple_spaces(self):
self.assertEqual(collapse_multiple_spaces("asdf"), "asdf")
self.assertEqual(collapse_multiple_spaces("as df"), "as df")
self.assertEqual(collapse_multiple_spaces("as df"), "as df")
self.assertEqual(collapse_multiple_spaces("a s d f"), "a s d f")
class TestFtpFunctions(unittest.TestCase):
def test_ftp_file_name_from_listing(self):
self.assertEqual(ftp_file_name_from_listing("-rw-rw-r-- 1 1176 1176 1063 Jun 15 10:18 README"), "README")
self.assertEqual(ftp_file_name_from_listing("-rw-rw-r-- 1 1176 1176 1063 Jun 15 10:18:12 README"), "README")
self.assertEqual(ftp_file_name_from_listing("-rw-rw-r-- 1 1176 1176 1063 Jun 15 10:18 READ ME"), "READ ME")
self.assertEqual(ftp_file_name_from_listing("-rw-rw-r-- 1 1176 1176 1063 Jun 15 10:18:12 READ ME"), "READ ME")
self.assertEqual(ftp_file_name_from_listing("-rw-rw-r-- 1 1176 1176 1063 Jun 15 10:18 README"), "README")
self.assertEqual(ftp_file_name_from_listing("-rw-rw-r-- 1 1176 1176 1063 Jun 15 10:18:12 README.txt"), "README.txt")
self.assertEqual(ftp_file_name_from_listing("-rw-rw-r-- 1 1176 1176 1063 Jun 15 10:18:12 READ ME.txt"), "READ ME.txt")
def test_ftp_list_dir_process_listing(self):
lines = [ "-rw-rw-r-- 1 1176 1176 1063 Jun 15 10:18 README",
"-rw-rw-r-- 1 1176 1176 1063 Jun 15 10:18:11 READ ME.txt",
"drwxr-sr-x 5 1176 1176 4096 Dec 19 2000 pool",
"drwxr-sr-x 4 1176 1176 4096 Nov 17 2008 project",
"drwxr-xr-x 3 1176 1176 4096 Oct 10 2012 tools"]
dirs_check = ["pool", "project", "tools"]
files_check = ["README", "READ ME.txt"]
dirs, files = ftp_list_dir_process_listing(lines)
dirs.sort()
dirs_check.sort()
files.sort()
files_check.sort()
self.assertEqual(dirs, dirs_check)
self.assertEqual(files, files_check)
class TestHttpFunctions(unittest.TestCase):
def test_http_links_from_listing(self):
html = """<html>
<head><title>Index of /debian/dists/</title></head>
<body bgcolor="white">
<a href="http://google.com">google/</a>
<h1>Index of /debian/dists/</h1><hr><pre><a href="../">../</a>
<a href="bunsen-hydrogen/">bunsen-hydrogen/</a> 08-May-2017 20:31 -
<a href="jessie-backports/">jessie-backports/</a> 01-Jul-2017 15:58 -
<a href="unstable/">unstable/</a> 12-Aug-2017 19:32 -
</pre><hr></body>
</html>"""
links_check = ["../", "bunsen-hydrogen/", "jessie-backports/", "unstable/", "http://google.com"]
links = http_links_from_listing(html)
links.sort()
links_check.sort()
self.assertEqual(links, links_check)
def test_http_paths_from_listing(self):
html = """<html>
<head><title>Index of /debian/dists/</title></head>
<body bgcolor="white">
<h1>Index of /debian/dists/</h1><hr><pre><a href="../">../</a>
<a href="bunsen-hydrogen/">bunsen-hydrogen/</a> 08-May-2017 20:31 -
<a href="jessie-backports/">jessie-backports/</a> 01-Jul-2017 15:58 -
<a href="unstable/">unstable/</a> 12-Aug-2017 19:32 -
</pre><hr></body>
</html>"""
paths_check = ["bunsen-hydrogen/", "jessie-backports/", "unstable/"]
paths = http_paths_from_listing(html)
paths.sort()
paths_check.sort()
self.assertEqual(paths, paths_check)
class TestPackageFunctions(unittest.TestCase):
def test_deb_packages_extract_version(self):
packages = """Package: sendmailanalyzer
Version: 9.2-1.1
Architecture: all
Maintainer: Dominique Fournier <dominique@fournier38.fr>
Installed-Size: 749
Pre-Depends: perl
Depends: apache2
Homepage: http://sareport.darold.net/
Priority: optional
Section: mail
Filename: pool/main/s/sendmailanalyzer/sendmailanalyzer_9.2-1.1_all.deb
Size: 143576
SHA256: 0edcbde19a23333c8c894e27af32447582b38e3ccd84122ac07720fdaab8fa0c
SHA1: a7f4dcf42e850acf2c201bc4594cb6b765dced20
MD5sum: adb39196fc33a826b24e9d0e440cba25
Description: Perl Sendmail/Postfix log analyser
SendmailAnalyzer continuously read your mail log file to generate
periodical HTML and graph reports. All reports are shown through
a CGI web interface.
It reports all you ever wanted to know about email trafic on your network.
You can also use it in ISP environment with per domain report.
Package: tint2
Version: 0.14.6-1
Architecture: amd64
Maintainer: Jens John <dev@2ion.de>
Installed-Size: 1230
Depends: libatk1.0-0 (>= 1.12.4), libc6 (>= 2.15), libcairo2 (>= 1.2.4), libfontconfig1 (>= 2.11), libfreetype6 (>= 2.2.1), libgdk-pixbuf2.0-0 (>= 2.22.0), libglib2.0-0 (>= 2.35.9), libgtk2.0-0 (>= 2.14.0), libimlib2 (>= 1.4.5), libpango-1.0-0 (>= 1.20.0), libpangocairo-1.0-0 (>= 1.14.0), libpangoft2-1.0-0 (>= 1.14.0), librsvg2-2 (>= 2.14.4), libstartup-notification0 (>= 0.4), libx11-6, libxcomposite1 (>= 1:0.3-1), libxdamage1 (>= 1:1.1), libxfixes3, libxinerama1, libxrandr2 (>= 2:1.2.99.3), libxrender1
Homepage: https://gitlab.com/o9000/tint2/
Priority: optional
Section: x11
Filename: pool/main/t/tint2/tint2_0.14.6-1_amd64.deb
Size: 279638
SHA256: c96e745425a97828952e9e0277176fe68e2512056915560ac968a66c88a0a8b7
SHA1: 82edd60429a494bb127e6d8a10434fca0ee60f61
MD5sum: 65455638fb41503361560b25a70b33b7
Description: lightweight taskbar
Tint is a simple panel/taskbar intentionally made for openbox3, but should
also work with other window managers. The taskbar includes transparency and
color settings for the font, icons, border, and background. It also supports
multihead setups, customized mouse actions, and a built-in clock. Tint was
originally based on ttm code. Since then, support has also been added
for a battery monitor and system tray.
.
The goal is to keep a clean and unintrusive look with lightweight code and
compliance with freedesktop specification.
Package: xfce4-power-manager
Version: 1.4.4-4~bpo8+1
Architecture: amd64
Maintainer: Debian Xfce Maintainers <pkg-xfce-devel@lists.alioth.debian.org>
Installed-Size: 541
Depends: libc6 (>= 2.4), libcairo2 (>= 1.2.4), libdbus-1-3 (>= 1.0.2), libdbus-glib-1-2 (>= 0.88), libgdk-pixbuf2.0-0 (>= 2.22.0), libglib2.0-0 (>= 2.41.1), libgtk2.0-0 (>= 2.24.0), libnotify4 (>= 0.7.0), libpango-1.0-0 (>= 1.14.0), libpangocairo-1.0-0 (>= 1.14.0), libupower-glib3 (>= 0.99.0), libx11-6, libxext6, libxfce4ui-1-0 (>= 4.9.0), libxfce4util6 (>= 4.9.0), libxfconf-0-2 (>= 4.6.0), libxrandr2 (>= 2:1.2.99.2), upower (>= 0.99), xfce4-power-manager-data (= 1.4.4-4~bpo8+1)
Recommends: libpam-systemd, xfce4-power-manager-plugins
Homepage: http://goodies.xfce.org/projects/applications/xfce4-power-manager
Priority: optional
Section: xfce
Filename: pool/main/x/xfce4-power-manager/xfce4-power-manager_1.4.4-4~bpo8+1_amd64.deb
Size: 214122
SHA256: 992b606afe5e9934bce19a1df2b8d7067c98b9d64e23a9b63dbd0c4cf28b4ac9
SHA1: 6bfcd77071f31577a37abab063bf21a34f4d616c
MD5sum: fb777aecbbfe39742649b768eb22c697
Description: power manager for Xfce desktop
This power manager for the Xfce desktop enables laptop users to set up
a power profile for two different modes "on battery power" and "on ac
power" while still allowing desktop users to at least change the DPMS
settings and CPU frequency using the settings dialogue..
.
It features:
* battery monitoring
* cpu frequency settings
* monitor DPMS settings
* suspend/Hibernate
* LCD brightness control
* Lid, sleep and power switches control"""
version, maintainer = deb_packages_extract_version(packages, "tint2")
self.assertEqual(version, "0.14.6-1")
self.assertEqual(maintainer, "Jens John <dev@2ion.de>")
version, maintainer = deb_packages_extract_version(packages, "asdf")
self.assertEqual(version, None)
self.assertEqual(maintainer, None)
def test_arch_packages_extract_version(self):
pkgbuild = """# $Id$
# Maintainer: Alexander F Rødseth <xyproto@archlinux.org>
# Contributor: Blue Peppers <bluepeppers@archlinux.us>
# Contributor: Stefan Husmann <stefan-husmann@t-online.de>
# Contributor: Yannick LM <LMyannicklm1337@gmail.com>
pkgname=tint2
pkgver=0.14.6
pkgrel=2
pkgdesc='Basic, good-looking task manager for WMs'
arch=('x86_64' 'i686')
url='https://gitlab.com/o9000/tint2'
license=('GPL2')
depends=('gtk2' 'imlib2' 'startup-notification')
makedepends=('cmake' 'startup-notification' 'git' 'ninja' 'setconf')
source=("$pkgname-$pkgver.tar.bz2::https://gitlab.com/o9000/tint2/repository/archive.tar.bz2?ref=$pkgver")
sha256sums=('b40079fb187aa248cd3b6957076f138d040c723b309e1b254ac0c8ec9826a451')
prepare() {
mv "$pkgname-$pkgver-"* "$pkgname"
setconf "$pkgname/get_version.sh" VERSION "$pkgver"
}
build() {
mkdir -p build
cd build
cmake "../$pkgname" \
-DCMAKE_INSTALL_PREFIX=/usr \
-DENABLE_TINT2CONF=1 \
-GNinja
ninja
}
package() {
DESTDIR="$pkgdir" ninja -C build install
}
# getver: gitlab.com/o9000/tint2/blob/master/README.md
# vim: ts=2 sw=2 et:"""
version, maintainer = arch_pkgbuild_extract_version(pkgbuild)
self.assertEqual(version, "0.14.6")
self.assertEqual(maintainer, "Alexander F Rødseth <xyproto@archlinux.org>")
if __name__ == '__main__':
unittest.main()

View File

@@ -35,20 +35,25 @@ gboolean bat1_has_font;
PangoFontDescription *bat1_font_desc;
gboolean bat2_has_font;
PangoFontDescription *bat2_font_desc;
char *bat1_format;
char *bat2_format;
struct BatteryState battery_state;
gboolean battery_enabled;
gboolean battery_tooltip_enabled;
int percentage_hide;
static timeout *battery_timeout;
static Timer battery_timer;
static char buf_bat_percentage[10];
static char buf_bat_time[20];
#define BATTERY_BUF_SIZE 256
static char buf_bat_line1[BATTERY_BUF_SIZE];
static char buf_bat_line2[BATTERY_BUF_SIZE];
int8_t battery_low_status;
gboolean battery_low_cmd_sent;
gboolean battery_full_cmd_sent;
char *ac_connected_cmd;
char *ac_disconnected_cmd;
char *battery_low_cmd;
char *battery_full_cmd;
char *battery_lclick_command;
char *battery_mclick_command;
char *battery_rclick_command;
@@ -70,14 +75,18 @@ void default_battery()
battery_found = FALSE;
percentage_hide = 101;
battery_low_cmd_sent = FALSE;
battery_timeout = NULL;
battery_full_cmd_sent = FALSE;
INIT_TIMER(battery_timer);
bat1_has_font = FALSE;
bat1_font_desc = NULL;
bat1_format = NULL;
bat2_has_font = FALSE;
bat2_font_desc = NULL;
bat2_format = NULL;
ac_connected_cmd = NULL;
ac_disconnected_cmd = NULL;
battery_low_cmd = NULL;
battery_full_cmd = NULL;
battery_lclick_command = NULL;
battery_mclick_command = NULL;
battery_rclick_command = NULL;
@@ -98,6 +107,12 @@ void cleanup_battery()
bat2_font_desc = NULL;
free(battery_low_cmd);
battery_low_cmd = NULL;
free(battery_full_cmd);
battery_full_cmd = NULL;
free(bat1_format);
bat1_format = NULL;
free(bat2_format);
bat2_format = NULL;
free(battery_lclick_command);
battery_lclick_command = NULL;
free(battery_mclick_command);
@@ -112,13 +127,97 @@ void cleanup_battery()
ac_connected_cmd = NULL;
free(ac_disconnected_cmd);
ac_disconnected_cmd = NULL;
stop_timeout(battery_timeout);
battery_timeout = NULL;
destroy_timer(&battery_timer);
battery_found = FALSE;
battery_os_free();
}
// Appends addendum to dest, and does not allow dest to grow over limit (including NULL terminator).
char *strnappend(char *dest, const char *addendum, size_t limit)
{
char *tmp = strdup(dest);
// Actually concatenate them.
snprintf(dest, limit, "%s%s", tmp, addendum);
free(tmp);
return dest;
}
void battery_update_text(char *dest, char *format)
{
if (!battery_enabled || !dest || !format)
return;
// We want to loop over the format specifier, replacing any known symbols with our battery data.
// First, erase anything already stored in the buffer.
// This ensures the string will always be null-terminated.
bzero(dest, BATTERY_BUF_SIZE);
for (size_t o = 0; o < strlen(format); o++) {
char buf[BATTERY_BUF_SIZE];
bzero(buf, BATTERY_BUF_SIZE);
char *c = &format[o];
// Format specification:
// %s : State (charging, discharging, full, unknown)
// %m : Minutes left (estimated).
// %h : Hours left (estimated).
// %t : Time left. This is equivalent to the old behaviour; i.e. "(plugged in)" or "hrs:mins" otherwise.
// %p : Percentage left. Includes the % sign.
if (*c == '%') {
c++;
o++; // Skip the format control character.
switch (*c) {
case 's':
// Append the appropriate status message to the string.
strnappend(dest,
(battery_state.state == BATTERY_CHARGING)
? "Charging"
: (battery_state.state == BATTERY_DISCHARGING)
? "Discharging"
: (battery_state.state == BATTERY_FULL)
? "Full"
: "Unknown",
BATTERY_BUF_SIZE);
break;
case 'm':
snprintf(buf, sizeof(buf), "%02d", battery_state.time.minutes);
strnappend(dest, buf, BATTERY_BUF_SIZE);
break;
case 'h':
snprintf(buf, sizeof(buf), "%02d", battery_state.time.hours);
strnappend(dest, buf, BATTERY_BUF_SIZE);
break;
case 'p':
snprintf(buf, sizeof(buf), "%d%%", battery_state.percentage);
strnappend(dest, buf, BATTERY_BUF_SIZE);
break;
case 't':
if (battery_state.state == BATTERY_FULL) {
snprintf(buf, sizeof(buf), "Full");
strnappend(dest, buf, BATTERY_BUF_SIZE);
} else if (battery_state.time.hours > 0 && battery_state.time.minutes > 0) {
snprintf(buf, sizeof(buf), "%02d:%02d", battery_state.time.hours, battery_state.time.minutes);
strnappend(dest, buf, BATTERY_BUF_SIZE);
}
break;
case '%':
case '\0':
strnappend(dest, "%", BATTERY_BUF_SIZE);
break;
default:
fprintf(stderr, "tint2: Battery: unrecognised format specifier '%%%c'.\n", *c);
buf[0] = *c;
strnappend(dest, buf, BATTERY_BUF_SIZE);
}
} else {
buf[0] = *c;
strnappend(dest, buf, BATTERY_BUF_SIZE);
}
}
}
void init_battery()
{
if (!battery_enabled)
@@ -126,8 +225,8 @@ void init_battery()
battery_found = battery_os_init();
if (!battery_timeout)
battery_timeout = add_timeout(10, 30000, update_battery_tick, 0, &battery_timeout);
if (!battery_timer.enabled_)
change_timer(&battery_timer, true, 30000, 30000, update_battery_tick, 0);
update_battery();
}
@@ -169,6 +268,12 @@ void init_battery_panel(void *p)
if (battery_tooltip_enabled)
battery->area._get_tooltip_text = battery_get_tooltip;
instantiate_area_gradients(&battery->area);
if (!bat1_format && !bat2_format) {
bat1_format = strdup("%p");
bat2_format = strdup("%t");
}
update_battery_tick(NULL);
}
void battery_init_fonts()
@@ -242,6 +347,16 @@ void update_battery_tick(void *arg)
battery_low_cmd_sent = FALSE;
}
if ((battery_state.percentage >= 100 || battery_state.state == BATTERY_FULL) &&
!battery_full_cmd_sent) {
tint_exec_no_sn(battery_full_cmd);
battery_full_cmd_sent = TRUE;
}
if (battery_state.percentage < 100 && battery_state.state != BATTERY_FULL &&
battery_full_cmd_sent) {
battery_full_cmd_sent = FALSE;
}
for (int i = 0; i < num_panels; i++) {
// Show/hide if needed
if (!battery_found) {
@@ -278,158 +393,53 @@ int update_battery()
battery_state.percentage = 100;
}
battery_update_text(buf_bat_line1, bat1_format);
if (bat2_format != 0) {
battery_update_text(buf_bat_line2, bat2_format);
}
return err;
}
int battery_compute_desired_size(void *obj)
{
Battery *battery = (Battery *)obj;
Panel *panel = (Panel *)battery->area.panel;
int bat_percentage_height, bat_percentage_width, bat_percentage_height_ink;
int bat_time_height, bat_time_width, bat_time_height_ink;
snprintf(buf_bat_percentage, sizeof(buf_bat_percentage), "%d%%", battery_state.percentage);
if (battery_state.state == BATTERY_FULL) {
strcpy(buf_bat_time, "Full");
} else {
snprintf(buf_bat_time, sizeof(buf_bat_time), "%02d:%02d", battery_state.time.hours, battery_state.time.minutes);
}
get_text_size2(bat1_font_desc,
&bat_percentage_height_ink,
&bat_percentage_height,
&bat_percentage_width,
panel->area.height,
panel->area.width,
buf_bat_percentage,
strlen(buf_bat_percentage),
PANGO_WRAP_WORD_CHAR,
PANGO_ELLIPSIZE_NONE,
FALSE);
get_text_size2(bat2_font_desc,
&bat_time_height_ink,
&bat_time_height,
&bat_time_width,
panel->area.height,
panel->area.width,
buf_bat_time,
strlen(buf_bat_time),
PANGO_WRAP_WORD_CHAR,
PANGO_ELLIPSIZE_NONE,
FALSE);
if (panel_horizontal) {
int new_size = (bat_percentage_width > bat_time_width) ? bat_percentage_width : bat_time_width;
new_size += 2 * battery->area.paddingxlr + left_right_border_width(&battery->area);
return new_size;
} else {
int new_size = bat_percentage_height + bat_time_height + 2 * battery->area.paddingxlr +
top_bottom_border_width(&battery->area);
return new_size;
}
return text_area_compute_desired_size(&battery->area, buf_bat_line1, buf_bat_line2, bat1_font_desc, bat2_font_desc);
}
gboolean resize_battery(void *obj)
{
Battery *battery = (Battery *)obj;
Panel *panel = (Panel *)battery->area.panel;
int bat_percentage_height, bat_percentage_width, bat_percentage_height_ink;
int bat_time_height, bat_time_width, bat_time_height_ink;
int ret = 0;
snprintf(buf_bat_percentage, sizeof(buf_bat_percentage), "%d%%", battery_state.percentage);
if (battery_state.state == BATTERY_FULL) {
strcpy(buf_bat_time, "Full");
} else {
snprintf(buf_bat_time, sizeof(buf_bat_time), "%02d:%02d", battery_state.time.hours, battery_state.time.minutes);
}
get_text_size2(bat1_font_desc,
&bat_percentage_height_ink,
&bat_percentage_height,
&bat_percentage_width,
panel->area.height,
panel->area.width,
buf_bat_percentage,
strlen(buf_bat_percentage),
PANGO_WRAP_WORD_CHAR,
PANGO_ELLIPSIZE_NONE,
FALSE);
get_text_size2(bat2_font_desc,
&bat_time_height_ink,
&bat_time_height,
&bat_time_width,
panel->area.height,
panel->area.width,
buf_bat_time,
strlen(buf_bat_time),
PANGO_WRAP_WORD_CHAR,
PANGO_ELLIPSIZE_NONE,
FALSE);
if (panel_horizontal) {
int new_size = (bat_percentage_width > bat_time_width) ? bat_percentage_width : bat_time_width;
new_size += 2 * battery->area.paddingxlr + left_right_border_width(&battery->area);
if (new_size > battery->area.width || new_size < battery->area.width - 2) {
// we try to limit the number of resize
battery->area.width = new_size;
battery->bat1_posy = (battery->area.height - bat_percentage_height - bat_time_height) / 2;
battery->bat2_posy = battery->bat1_posy + bat_percentage_height;
ret = 1;
}
} else {
int new_size = bat_percentage_height + bat_time_height + 2 * battery->area.paddingxlr +
top_bottom_border_width(&battery->area);
if (new_size > battery->area.height || new_size < battery->area.height - 2) {
battery->area.height = new_size;
battery->bat1_posy = (battery->area.height - bat_percentage_height - bat_time_height - 2) / 2;
battery->bat2_posy = battery->bat1_posy + bat_percentage_height + 2;
ret = 1;
}
}
schedule_redraw(&battery->area);
return ret;
return resize_text_area(&battery->area,
buf_bat_line1,
buf_bat_line2,
bat1_font_desc,
bat2_font_desc,
&battery->bat1_posy,
&battery->bat2_posy);
}
void draw_battery(void *obj, cairo_t *c)
{
Battery *battery = obj;
PangoLayout *layout = pango_cairo_create_layout(c);
pango_layout_set_font_description(layout, bat1_font_desc);
pango_layout_set_width(layout, battery->area.width * PANGO_SCALE);
pango_layout_set_alignment(layout, PANGO_ALIGN_CENTER);
pango_layout_set_wrap(layout, PANGO_WRAP_WORD_CHAR);
pango_layout_set_ellipsize(layout, PANGO_ELLIPSIZE_NONE);
pango_layout_set_text(layout, buf_bat_percentage, strlen(buf_bat_percentage));
cairo_set_source_rgba(c,
battery->font_color.rgb[0],
battery->font_color.rgb[1],
battery->font_color.rgb[2],
battery->font_color.alpha);
pango_cairo_update_layout(c, layout);
draw_text(layout, c, 0, battery->bat1_posy, &battery->font_color, ((Panel *)battery->area.panel)->font_shadow);
pango_layout_set_font_description(layout, bat2_font_desc);
pango_layout_set_indent(layout, 0);
pango_layout_set_wrap(layout, PANGO_WRAP_WORD_CHAR);
pango_layout_set_ellipsize(layout, PANGO_ELLIPSIZE_NONE);
pango_layout_set_text(layout, buf_bat_time, strlen(buf_bat_time));
pango_layout_set_width(layout, battery->area.width * PANGO_SCALE);
pango_cairo_update_layout(c, layout);
draw_text(layout, c, 0, battery->bat2_posy, &battery->font_color, ((Panel *)battery->area.panel)->font_shadow);
pango_cairo_show_layout(c, layout);
g_object_unref(layout);
Battery *battery = (Battery *)obj;
Panel *panel = (Panel *)battery->area.panel;
draw_text_area(&battery->area,
c,
buf_bat_line1,
buf_bat_line2,
bat1_font_desc,
bat2_font_desc,
battery->bat1_posy,
battery->bat2_posy,
&battery->font_color,
panel->scale);
}
void battery_dump_geometry(void *obj, int indent)
{
Battery *battery = obj;
fprintf(stderr, "%*sText 1: y = %d, text = %s\n", indent, "", battery->bat1_posy, buf_bat_percentage);
fprintf(stderr, "%*sText 2: y = %d, text = %s\n", indent, "", battery->bat2_posy, buf_bat_time);
Battery *battery = (Battery *)obj;
fprintf(stderr, "tint2: %*sText 1: y = %d, text = %s\n", indent, "", battery->bat1_posy, buf_bat_line1);
fprintf(stderr, "tint2: %*sText 2: y = %d, text = %s\n", indent, "", battery->bat2_posy, buf_bat_line2);
}
char *battery_get_tooltip(void *obj)
@@ -437,7 +447,7 @@ char *battery_get_tooltip(void *obj)
return battery_os_tooltip();
}
void battery_action(int button, Time time)
void battery_action(void *obj, int button, int x, int y, Time time)
{
char *command = NULL;
switch (button) {
@@ -457,5 +467,5 @@ void battery_action(int button, Time time)
command = battery_dwheel_command;
break;
}
tint_exec(command, NULL, NULL, time);
tint_exec(command, NULL, NULL, time, obj, x, y, FALSE, TRUE);
}

View File

@@ -48,12 +48,15 @@ extern gboolean bat1_has_font;
extern PangoFontDescription *bat1_font_desc;
extern gboolean bat2_has_font;
extern PangoFontDescription *bat2_font_desc;
extern char *bat1_format;
extern char *bat2_format;
extern gboolean battery_enabled;
extern gboolean battery_tooltip_enabled;
extern int percentage_hide;
extern int8_t battery_low_status;
extern char *battery_low_cmd;
extern char *battery_full_cmd;
extern char *ac_connected_cmd;
extern char *ac_disconnected_cmd;
@@ -108,7 +111,7 @@ void battery_default_font_changed();
gboolean resize_battery(void *obj);
void battery_action(int button, Time time);
void battery_action(void *obj, int button, int x, int y, Time time);
/* operating system specific functions */
gboolean battery_os_init();

View File

@@ -58,7 +58,7 @@ int battery_os_update(BatteryState *state)
break;
}
} else {
fprintf(stderr, "power update: no such sysctl");
fprintf(stderr, "tint2: power update: no such sysctl");
err = -1;
}

View File

@@ -60,6 +60,20 @@ struct psy_mains {
gboolean online;
};
static gboolean is_file_non_empty(const char *path)
{
FILE *f = fopen(path, "r");
if (!f)
return FALSE;
char buffer[1024];
size_t count = fread(buffer, 1, sizeof(buffer), f);
fclose(f);
if (count > 0)
return TRUE;
else
return FALSE;
}
static void uevent_battery_update()
{
update_battery_tick(NULL);
@@ -68,16 +82,16 @@ static struct uevent_notify psy_change = {UEVENT_CHANGE, "power_supply", NULL, u
static void uevent_battery_plug()
{
printf("reinitialize batteries after HW change\n");
fprintf(stderr, "tint2: reinitialize batteries after HW change\n");
reinit_battery();
}
static struct uevent_notify psy_plug = {UEVENT_ADD | UEVENT_REMOVE, "power_supply", NULL, uevent_battery_plug};
#define RETURN_ON_ERROR(err) \
if (err) { \
g_error_free(err); \
fprintf(stderr, RED "%s:%d: errror" RESET "\n", __FILE__, __LINE__); \
return FALSE; \
#define RETURN_ON_ERROR(err) \
if (err) { \
g_error_free(err); \
fprintf(stderr, RED "tint2: %s:%d: errror" RESET "\n", __FILE__, __LINE__); \
return FALSE; \
}
static GList *batteries = NULL;
@@ -96,12 +110,13 @@ static enum psy_type power_supply_get_type(const gchar *entryname)
gsize typelen;
g_file_get_contents(path_type, &type, &typelen, &error);
g_free(path_type);
if (error) {
fprintf(stderr, RED "%s:%d: read failed" RESET "\n", __FILE__, __LINE__);
fprintf(stderr, RED "tint2: %s:%d: read failed for %s" RESET "\n", __FILE__, __LINE__, path_type);
g_free(path_type);
g_error_free(error);
return PSY_UNKNOWN;
}
g_free(path_type);
if (!g_strcmp0(type, "Battery\n")) {
g_free(type);
@@ -123,8 +138,8 @@ static gboolean init_linux_battery(struct psy_battery *bat)
const gchar *entryname = bat->name;
bat->path_present = g_build_filename(battery_sys_prefix, "/sys/class/power_supply", entryname, "present", NULL);
if (!g_file_test(bat->path_present, G_FILE_TEST_EXISTS)) {
fprintf(stderr, RED "%s:%d: read failed" RESET "\n", __FILE__, __LINE__);
if (!is_file_non_empty(bat->path_present)) {
fprintf(stderr, RED "tint2: %s:%d: read failed for %s" RESET "\n", __FILE__, __LINE__, bat->path_present);
goto err0;
}
@@ -135,9 +150,8 @@ static gboolean init_linux_battery(struct psy_battery *bat)
bat->path_rate_now = g_build_filename(battery_sys_prefix, "/sys/class/power_supply", entryname, "power_now", NULL);
bat->unit = 'W';
if (!g_file_test(bat->path_level_now, G_FILE_TEST_EXISTS) ||
!g_file_test(bat->path_level_full, G_FILE_TEST_EXISTS) ||
!g_file_test(bat->path_rate_now, G_FILE_TEST_EXISTS)) {
if (!is_file_non_empty(bat->path_level_now) ||
!is_file_non_empty(bat->path_level_full)) {
g_free(bat->path_level_now);
g_free(bat->path_level_full);
g_free(bat->path_rate_now);
@@ -149,16 +163,18 @@ static gboolean init_linux_battery(struct psy_battery *bat)
g_build_filename(battery_sys_prefix, "/sys/class/power_supply", entryname, "current_now", NULL);
bat->unit = 'A';
}
if (!g_file_test(bat->path_level_now, G_FILE_TEST_EXISTS) ||
!g_file_test(bat->path_level_full, G_FILE_TEST_EXISTS) ||
!g_file_test(bat->path_rate_now, G_FILE_TEST_EXISTS)) {
fprintf(stderr, RED "%s:%d: read failed" RESET "\n", __FILE__, __LINE__);
if (!is_file_non_empty(bat->path_level_now)) {
fprintf(stderr, RED "tint2: %s:%d: read failed for %s" RESET "\n", __FILE__, __LINE__, bat->path_level_now);
goto err1;
}
if (!is_file_non_empty(bat->path_level_full)) {
fprintf(stderr, RED "tint2: %s:%d: read failed for %s" RESET "\n", __FILE__, __LINE__, bat->path_level_full);
goto err1;
}
bat->path_status = g_build_filename(battery_sys_prefix, "/sys/class/power_supply", entryname, "status", NULL);
if (!g_file_test(bat->path_status, G_FILE_TEST_EXISTS)) {
fprintf(stderr, RED "%s:%d: read failed" RESET "\n", __FILE__, __LINE__);
if (!is_file_non_empty(bat->path_status)) {
fprintf(stderr, RED "tint2: %s:%d: read failed for %s" RESET "\n", __FILE__, __LINE__, bat->path_status);
goto err2;
}
@@ -181,8 +197,8 @@ static gboolean init_linux_mains(struct psy_mains *ac)
const gchar *entryname = ac->name;
ac->path_online = g_build_filename(battery_sys_prefix, "/sys/class/power_supply", entryname, "online", NULL);
if (!g_file_test(ac->path_online, G_FILE_TEST_EXISTS)) {
fprintf(stderr, RED "%s:%d: read failed" RESET "\n", __FILE__, __LINE__);
if (!is_file_non_empty(ac->path_online)) {
fprintf(stderr, RED "tint2: %s:%d: read failed for %s" RESET "\n", __FILE__, __LINE__, ac->path_online);
g_free(ac->path_online);
return FALSE;
}
@@ -228,10 +244,10 @@ static void add_battery(const char *entryname)
if (init_linux_battery(bat)) {
batteries = g_list_append(batteries, bat);
fprintf(stdout, GREEN "Found battery \"%s\"" RESET "\n", bat->name);
fprintf(stderr, GREEN "Found battery \"%s\"" RESET "\n", bat->name);
} else {
g_free(bat);
fprintf(stderr, RED "Failed to initialize battery \"%s\"" RESET "\n", entryname);
fprintf(stderr, RED "tint2: Failed to initialize battery \"%s\"" RESET "\n", entryname);
}
}
@@ -242,10 +258,10 @@ static void add_mains(const char *entryname)
if (init_linux_mains(ac)) {
mains = g_list_append(mains, ac);
fprintf(stdout, GREEN "Found mains \"%s\"" RESET "\n", ac->name);
fprintf(stderr, GREEN "Found mains \"%s\"" RESET "\n", ac->name);
} else {
g_free(ac);
fprintf(stderr, RED "Failed to initialize mains \"%s\"" RESET "\n", entryname);
fprintf(stderr, RED "tint2: Failed to initialize mains \"%s\"" RESET "\n", entryname);
}
}
@@ -263,7 +279,7 @@ gboolean battery_os_init()
RETURN_ON_ERROR(error);
while ((entryname = g_dir_read_name(directory))) {
fprintf(stderr, GREEN "Found power device %s" RESET "\n", entryname);
fprintf(stderr, GREEN "tint2: Found power device %s" RESET "\n", entryname);
enum psy_type type = power_supply_get_type(entryname);
switch (type) {
@@ -446,6 +462,17 @@ int battery_os_update(BatteryState *state)
/* AC state */
state->ac_connected = ac_connected;
if (state->state == BATTERY_UNKNOWN) {
if (ac_connected) {
if (total_rate_now == 0 && state->percentage >= 90)
state->state = BATTERY_FULL;
else
state->state = BATTERY_CHARGING;
} else {
state->state = BATTERY_DISCHARGING;
}
}
return 0;
}

View File

@@ -52,11 +52,9 @@ void destroy_button(void *obj)
Button *button = (Button *)obj;
if (button->frontend) {
// This is a frontend element
if (button->frontend->icon) {
imlib_context_set_image(button->frontend->icon);
imlib_free_image();
button->frontend->icon = NULL;
}
free_icon(button->frontend->icon);
free_icon(button->frontend->icon_hover);
free_icon(button->frontend->icon_pressed);
button->backend->instances = g_list_remove_all(button->backend->instances, button);
free_and_null(button->frontend);
remove_area(&button->area);
@@ -78,8 +76,8 @@ void destroy_button(void *obj)
free_and_null(button->backend->uwheel_command);
if (button->backend->instances) {
fprintf(stderr, "Error: Attempt to destroy backend while there are still frontend instances!\n");
exit(-1);
fprintf(stderr, "tint2: Error: Attempt to destroy backend while there are still frontend instances!\n");
exit(EXIT_FAILURE);
}
free(button->backend);
free(button);
@@ -271,9 +269,9 @@ int button_compute_desired_size(void *obj)
{
Button *button = (Button *)obj;
Panel *panel = (Panel *)button->area.panel;
int horiz_padding = (panel_horizontal ? button->area.paddingxlr : button->area.paddingy);
int vert_padding = (panel_horizontal ? button->area.paddingy : button->area.paddingxlr);
int interior_padding = button->area.paddingx;
int horiz_padding = (panel_horizontal ? button->area.paddingxlr : button->area.paddingy) * panel->scale;
int vert_padding = (panel_horizontal ? button->area.paddingy : button->area.paddingxlr) * panel->scale;
int interior_padding = button->area.paddingx * panel->scale;
int icon_w, icon_h;
if (button->backend->icon_name) {
@@ -282,18 +280,17 @@ int button_compute_desired_size(void *obj)
else
icon_h = icon_w = button->area.width - left_right_border_width(&button->area) - 2 * horiz_padding;
if (button->backend->max_icon_size) {
icon_w = MIN(icon_w, button->backend->max_icon_size);
icon_h = MIN(icon_h, button->backend->max_icon_size);
icon_w = MIN(icon_w, button->backend->max_icon_size * panel->scale);
icon_h = MIN(icon_h, button->backend->max_icon_size * panel->scale);
}
} else {
icon_h = icon_w = 0;
}
int txt_height_ink, txt_height, txt_width;
int txt_height, txt_width;
if (button->backend->text) {
if (panel_horizontal) {
get_text_size2(button->backend->font_desc,
&txt_height_ink,
&txt_height,
&txt_width,
panel->area.height,
@@ -302,10 +299,11 @@ int button_compute_desired_size(void *obj)
strlen(button->backend->text),
PANGO_WRAP_WORD_CHAR,
PANGO_ELLIPSIZE_NONE,
FALSE);
button->backend->centered ? PANGO_ALIGN_CENTER : PANGO_ALIGN_LEFT,
FALSE,
panel->scale);
} else {
get_text_size2(button->backend->font_desc,
&txt_height_ink,
&txt_height,
&txt_width,
panel->area.height,
@@ -315,10 +313,12 @@ int button_compute_desired_size(void *obj)
strlen(button->backend->text),
PANGO_WRAP_WORD_CHAR,
PANGO_ELLIPSIZE_NONE,
FALSE);
button->backend->centered ? PANGO_ALIGN_CENTER : PANGO_ALIGN_LEFT,
FALSE,
panel->scale);
}
} else {
txt_height_ink = txt_height = txt_width = 0;
txt_height = txt_width = 0;
}
if (panel_horizontal) {
@@ -337,9 +337,10 @@ gboolean resize_button(void *obj)
{
Button *button = (Button *)obj;
Panel *panel = (Panel *)button->area.panel;
int horiz_padding = (panel_horizontal ? button->area.paddingxlr : button->area.paddingy);
int vert_padding = (panel_horizontal ? button->area.paddingy : button->area.paddingxlr);
int interior_padding = button->area.paddingx;
Area *area = &button->area;
int horiz_padding = (panel_horizontal ? button->area.paddingxlr : button->area.paddingy) * panel->scale;
int vert_padding = (panel_horizontal ? button->area.paddingy : button->area.paddingxlr) * panel->scale;
int interior_padding = button->area.paddingx * panel->scale;
int icon_w, icon_h;
if (button->backend->icon_name) {
@@ -348,8 +349,8 @@ gboolean resize_button(void *obj)
else
icon_h = icon_w = button->area.width - left_right_border_width(&button->area) - 2 * horiz_padding;
if (button->backend->max_icon_size) {
icon_w = MIN(icon_w, button->backend->max_icon_size);
icon_h = MIN(icon_h, button->backend->max_icon_size);
icon_w = MIN(icon_w, button->backend->max_icon_size * panel->scale);
icon_h = MIN(icon_h, button->backend->max_icon_size * panel->scale);
}
} else {
icon_h = icon_w = 0;
@@ -360,36 +361,32 @@ gboolean resize_button(void *obj)
if (button->frontend->icon_load_size != button->frontend->iconw)
button_reload_icon(button);
int txt_height_ink, txt_height, txt_width;
if (button->backend->text) {
if (panel_horizontal) {
get_text_size2(button->backend->font_desc,
&txt_height_ink,
&txt_height,
&txt_width,
panel->area.height,
panel->area.width,
button->backend->text,
strlen(button->backend->text),
PANGO_WRAP_WORD_CHAR,
PANGO_ELLIPSIZE_NONE,
FALSE);
} else {
get_text_size2(button->backend->font_desc,
&txt_height_ink,
&txt_height,
&txt_width,
panel->area.height,
button->area.width - icon_w - (icon_w ? interior_padding : 0) - 2 * horiz_padding -
left_right_border_width(&button->area),
button->backend->text,
strlen(button->backend->text),
PANGO_WRAP_WORD_CHAR,
PANGO_ELLIPSIZE_NONE,
FALSE);
}
int available_w, available_h;
if (panel_horizontal) {
available_w = panel->area.width;
available_h = area->height - 2 * area->paddingy - left_right_border_width(area);
} else {
txt_height_ink = txt_height = txt_width = 0;
available_w =
area->width - icon_w - (icon_w ? interior_padding : 0) - 2 * horiz_padding - left_right_border_width(area);
available_h = panel->area.height;
}
int txt_height, txt_width;
if (button->backend->text) {
get_text_size2(button->backend->font_desc,
&txt_height,
&txt_width,
available_h,
available_w,
button->backend->text,
strlen(button->backend->text),
PANGO_WRAP_WORD_CHAR,
PANGO_ELLIPSIZE_NONE,
button->backend->centered ? PANGO_ALIGN_CENTER : PANGO_ALIGN_LEFT,
FALSE,
panel->scale);
} else {
txt_height = txt_width = 0;
}
gboolean result = FALSE;
@@ -442,6 +439,7 @@ gboolean resize_button(void *obj)
void draw_button(void *obj, cairo_t *c)
{
Button *button = obj;
Panel *panel = (Panel *)button->area.panel;
if (button->frontend->icon) {
// Render icon
@@ -463,10 +461,12 @@ void draw_button(void *obj, cairo_t *c)
// Render text
if (button->backend->text) {
PangoLayout *layout = pango_cairo_create_layout(c);
PangoContext *context = pango_cairo_create_context(c);
pango_cairo_context_set_resolution(context, 96 * panel->scale);
PangoLayout *layout = pango_layout_new(context);
pango_layout_set_font_description(layout, button->backend->font_desc);
pango_layout_set_width(layout, button->frontend->textw * PANGO_SCALE);
pango_layout_set_width(layout, (button->frontend->textw + TINT2_PANGO_SLACK) * PANGO_SCALE);
pango_layout_set_alignment(layout, button->backend->centered ? PANGO_ALIGN_CENTER : PANGO_ALIGN_LEFT);
pango_layout_set_wrap(layout, PANGO_WRAP_WORD_CHAR);
pango_layout_set_ellipsize(layout, PANGO_ELLIPSIZE_NONE);
@@ -481,6 +481,7 @@ void draw_button(void *obj, cairo_t *c)
panel_config.font_shadow);
g_object_unref(layout);
g_object_unref(context);
}
}
@@ -492,7 +493,7 @@ void button_dump_geometry(void *obj, int indent)
Imlib_Image tmp = imlib_context_get_image();
imlib_context_set_image(button->frontend->icon);
fprintf(stderr,
"%*sIcon: x = %d, y = %d, w = %d, h = %d\n",
"tint2: %*sIcon: x = %d, y = %d, w = %d, h = %d\n",
indent,
"",
button->frontend->iconx,
@@ -503,7 +504,7 @@ void button_dump_geometry(void *obj, int indent)
imlib_context_set_image(tmp);
}
fprintf(stderr,
"%*sText: x = %d, y = %d, w = %d, align = %s, text = %s\n",
"tint2: %*sText: x = %d, y = %d, w = %d, align = %s, text = %s\n",
indent,
"",
button->frontend->textx,
@@ -516,7 +517,6 @@ void button_dump_geometry(void *obj, int indent)
void button_action(void *obj, int mouse_button, int x, int y, Time time)
{
Button *button = (Button *)obj;
Panel *panel = (Panel *)button->area.panel;
char *command = NULL;
switch (mouse_button) {
case 1:
@@ -535,100 +535,7 @@ void button_action(void *obj, int mouse_button, int x, int y, Time time)
command = button->backend->dwheel_command;
break;
}
if (command) {
int aligned_x, aligned_y, aligned_x1, aligned_y1, aligned_x2, aligned_y2;
int panel_x1, panel_x2, panel_y1, panel_y2;
if (panel_horizontal) {
if (area_is_first(button))
aligned_x1 = panel->posx;
else
aligned_x1 = panel->posx + button->area.posx;
if (area_is_last(button))
aligned_x2 = panel->posx + panel->area.width;
else
aligned_x2 = panel->posx + button->area.posx + button->area.width;
if (area_is_first(button))
aligned_x = aligned_x1;
else if (area_is_last(button))
aligned_x = aligned_x2;
else
aligned_x = aligned_x1;
if (panel_position & BOTTOM)
aligned_y = panel->posy;
else
aligned_y = panel->posy + panel->area.height;
aligned_y1 = aligned_y2 = aligned_y;
panel_x1 = panel->posx;
panel_x2 = panel->posx + panel->area.width;
panel_y1 = panel_y2 = aligned_y;
} else {
if (area_is_first(button))
aligned_y1 = panel->posy;
else
aligned_y1 = panel->posy + button->area.posy;
if (area_is_last(button))
aligned_y2 = panel->posy + panel->area.height;
else
aligned_y2 = panel->posy + button->area.posy + button->area.height;
if (area_is_first(button))
aligned_y = aligned_y1;
else if (area_is_last(button))
aligned_y = aligned_y2;
else
aligned_y = aligned_y1;
if (panel_position & RIGHT)
aligned_x = panel->posx;
else
aligned_x = panel->posx + panel->area.width;
aligned_x1 = aligned_x2 = aligned_x;
panel_x1 = panel_x2 = aligned_x;
panel_y1 = panel->posy;
panel_y2 = panel->posy + panel->area.height;
}
char *full_cmd = g_strdup_printf("export TINT2_BUTTON_X=%d;"
"export TINT2_BUTTON_Y=%d;"
"export TINT2_BUTTON_W=%d;"
"export TINT2_BUTTON_H=%d;"
"export TINT2_BUTTON_ALIGNED_X=%d;"
"export TINT2_BUTTON_ALIGNED_Y=%d;"
"export TINT2_BUTTON_ALIGNED_X1=%d;"
"export TINT2_BUTTON_ALIGNED_Y1=%d;"
"export TINT2_BUTTON_ALIGNED_X2=%d;"
"export TINT2_BUTTON_ALIGNED_Y2=%d;"
"export TINT2_BUTTON_PANEL_X1=%d;"
"export TINT2_BUTTON_PANEL_Y1=%d;"
"export TINT2_BUTTON_PANEL_X2=%d;"
"export TINT2_BUTTON_PANEL_Y2=%d;"
"%s",
x,
y,
button->area.width,
button->area.height,
aligned_x,
aligned_y,
aligned_x1,
aligned_y1,
aligned_x2,
aligned_y2,
panel_x1,
panel_y1,
panel_x2,
panel_y2,
command);
tint_exec(full_cmd, NULL, NULL, time);
g_free(full_cmd);
}
tint_exec(command, NULL, NULL, time, obj, x, y, FALSE, TRUE);
}
char *button_get_tooltip(void *obj)

View File

@@ -51,7 +51,7 @@ static char buf_time[256];
static char buf_date[256];
static char buf_tooltip[512];
int clock_enabled;
static timeout *clock_timeout;
static Timer clock_timer;
void clock_init_fonts();
char *clock_get_tooltip(void *obj);
@@ -61,11 +61,11 @@ void clock_dump_geometry(void *obj, int indent);
void default_clock()
{
clock_enabled = 0;
clock_timeout = NULL;
time1_format = NULL;
time1_timezone = NULL;
time2_format = NULL;
time2_timezone = NULL;
INIT_TIMER(clock_timer);
time_tooltip_format = NULL;
time_tooltip_timezone = NULL;
clock_lclick_command = NULL;
@@ -77,6 +77,9 @@ void default_clock()
time1_font_desc = NULL;
time2_has_font = FALSE;
time2_font_desc = NULL;
buf_time[0] = 0;
buf_date[0] = 0;
buf_tooltip[0] = 0;
}
void cleanup_clock()
@@ -107,33 +110,7 @@ void cleanup_clock()
clock_uwheel_command = NULL;
free(clock_dwheel_command);
clock_dwheel_command = NULL;
stop_timeout(clock_timeout);
clock_timeout = NULL;
}
void update_clocks_sec(void *arg)
{
gettimeofday(&time_clock, 0);
if (time1_format) {
for (int i = 0; i < num_panels; i++)
panels[i].clock.area.resize_needed = 1;
}
schedule_panel_redraw();
}
void update_clocks_min(void *arg)
{
// remember old_sec because after suspend/hibernate the clock should be updated directly, and not
// on next minute change
time_t old_sec = time_clock.tv_sec;
gettimeofday(&time_clock, 0);
if (time_clock.tv_sec % 60 == 0 || time_clock.tv_sec - old_sec > 60) {
if (time1_format) {
for (int i = 0; i < num_panels; i++)
panels[i].clock.area.resize_needed = 1;
}
schedule_panel_redraw();
}
destroy_timer(&clock_timer);
}
struct tm *clock_gettime_for_tz(const char *timezone)
@@ -152,6 +129,46 @@ struct tm *clock_gettime_for_tz(const char *timezone)
}
}
void update_clocks()
{
if (time1_format)
strftime(buf_time, sizeof(buf_time), time1_format, clock_gettime_for_tz(time1_timezone));
if (time2_format)
strftime(buf_date, sizeof(buf_date), time2_format, clock_gettime_for_tz(time2_timezone));
if (time1_format || time2_format) {
for (int i = 0; i < num_panels; i++)
panels[i].clock.area.resize_needed = 1;
}
schedule_panel_redraw();
}
int ms_until_second_change(struct timeval* tm)
{
long us_until_change = 1000000 - tm->tv_usec;
// compute ms, rounding up so we don't ask to wait too short
int ms = (us_until_change+999)/1000;
return ms;
}
void update_clocks_sec(void *arg)
{
gettimeofday(&time_clock, 0);
update_clocks();
change_timer(&clock_timer, true, ms_until_second_change(&time_clock), 0, update_clocks_sec, 0);
}
void update_clocks_min(void *arg)
{
// remember old_sec because after suspend/hibernate the clock should be updated directly, and not
// on next minute change
static time_t old_sec = 0;
gettimeofday(&time_clock, 0);
if (time_clock.tv_sec % 60 == 0 || time_clock.tv_sec - old_sec > 60 || (time1_format && !buf_time[0]) || (time2_format && !buf_date[0]))
update_clocks();
old_sec = time_clock.tv_sec;
change_timer(&clock_timer, true, ms_until_second_change(&time_clock), 0, update_clocks_min, 0);
}
gboolean time_format_needs_sec_ticks(char *time_format)
{
if (!time_format)
@@ -170,14 +187,6 @@ void init_clock_panel(void *p)
Panel *panel = (Panel *)p;
Clock *clock = &panel->clock;
if (!clock_timeout) {
if (time_format_needs_sec_ticks(time1_format) || time_format_needs_sec_ticks(time2_format)) {
clock_timeout = add_timeout(10, 1000, update_clocks_sec, 0, &clock_timeout);
} else {
clock_timeout = add_timeout(10, 1000, update_clocks_min, 0, &clock_timeout);
}
}
if (!clock->area.bg)
clock->area.bg = &g_array_index(backgrounds, Background, 0);
clock_init_fonts();
@@ -205,6 +214,14 @@ void init_clock_panel(void *p)
clock->area._get_tooltip_text = clock_get_tooltip;
strftime(buf_tooltip, sizeof(buf_tooltip), time_tooltip_format, clock_gettime_for_tz(time_tooltip_timezone));
}
if (!clock_timer.enabled_) {
if (time_format_needs_sec_ticks(time1_format) || time_format_needs_sec_ticks(time2_format)) {
update_clocks_sec(NULL);
} else {
update_clocks_min(NULL);
}
}
}
void clock_init_fonts()
@@ -243,147 +260,67 @@ void clock_default_font_changed()
schedule_panel_redraw();
}
void clock_compute_text_geometry(Panel *panel,
int *time_height_ink,
void clock_compute_text_geometry(Clock *clock,
int *time_height,
int *time_width,
int *date_height_ink,
int *date_height,
int *date_width)
{
*date_height = *date_width = 0;
strftime(buf_time, sizeof(buf_time), time1_format, clock_gettime_for_tz(time1_timezone));
get_text_size2(time1_font_desc,
time_height_ink,
time_height,
time_width,
panel->area.height,
panel->area.width,
buf_time,
strlen(buf_time),
PANGO_WRAP_WORD_CHAR,
PANGO_ELLIPSIZE_NONE,
FALSE);
if (time2_format) {
strftime(buf_date, sizeof(buf_date), time2_format, clock_gettime_for_tz(time2_timezone));
get_text_size2(time2_font_desc,
date_height_ink,
date_height,
date_width,
panel->area.height,
panel->area.width,
buf_date,
strlen(buf_date),
PANGO_WRAP_WORD_CHAR,
PANGO_ELLIPSIZE_NONE,
FALSE);
}
area_compute_text_geometry(&clock->area,
buf_time,
time2_format ? buf_date : NULL,
time1_font_desc,
time2_font_desc,
time_height,
time_width,
date_height,
date_width);
}
int clock_compute_desired_size(void *obj)
{
Clock *clock = (Clock *)obj;
Panel *panel = (Panel *)clock->area.panel;
int time_height_ink, time_height, time_width, date_height_ink, date_height, date_width;
clock_compute_text_geometry(panel,
&time_height_ink,
&time_height,
&time_width,
&date_height_ink,
&date_height,
&date_width);
if (panel_horizontal) {
int new_size = (time_width > date_width) ? time_width : date_width;
new_size += 2 * clock->area.paddingxlr + left_right_border_width(&clock->area);
return new_size;
} else {
int new_size = time_height + date_height + 2 * clock->area.paddingxlr + top_bottom_border_width(&clock->area);
return new_size;
}
return text_area_compute_desired_size(&clock->area,
buf_time,
time2_format ? buf_date : NULL,
time1_font_desc,
time2_font_desc);
}
gboolean resize_clock(void *obj)
{
Clock *clock = (Clock *)obj;
Panel *panel = (Panel *)clock->area.panel;
gboolean result = FALSE;
schedule_redraw(&clock->area);
int time_height_ink, time_height, time_width, date_height_ink, date_height, date_width;
clock_compute_text_geometry(panel,
&time_height_ink,
&time_height,
&time_width,
&date_height_ink,
&date_height,
&date_width);
int new_size = clock_compute_desired_size(clock);
if (panel_horizontal) {
if (new_size > clock->area.width || new_size < (clock->area.width - 6)) {
// we try to limit the number of resizes
clock->area.width = new_size + 1;
clock->time1_posy = (clock->area.height - time_height) / 2;
if (time2_format) {
clock->time1_posy -= (date_height) / 2;
clock->time2_posy = clock->time1_posy + time_height;
}
result = TRUE;
}
} else {
if (new_size != clock->area.height) {
// we try to limit the number of resizes
clock->area.height = new_size;
clock->time1_posy = (clock->area.height - time_height) / 2;
if (time2_format) {
clock->time1_posy -= (date_height) / 2;
clock->time2_posy = clock->time1_posy + time_height;
}
result = TRUE;
}
}
return result;
return resize_text_area(&clock->area,
buf_time,
time2_format ? buf_date : NULL,
time1_font_desc,
time2_font_desc,
&clock->time1_posy,
&clock->time2_posy);
}
void draw_clock(void *obj, cairo_t *c)
{
Clock *clock = obj;
PangoLayout *layout = pango_cairo_create_layout(c);
pango_layout_set_font_description(layout, time1_font_desc);
pango_layout_set_width(layout, clock->area.width * PANGO_SCALE);
pango_layout_set_alignment(layout, PANGO_ALIGN_CENTER);
pango_layout_set_wrap(layout, PANGO_WRAP_WORD_CHAR);
pango_layout_set_ellipsize(layout, PANGO_ELLIPSIZE_NONE);
pango_layout_set_text(layout, buf_time, strlen(buf_time));
cairo_set_source_rgba(c, clock->font.rgb[0], clock->font.rgb[1], clock->font.rgb[2], clock->font.alpha);
pango_cairo_update_layout(c, layout);
draw_text(layout, c, 0, clock->time1_posy, &clock->font, ((Panel *)clock->area.panel)->font_shadow);
if (time2_format) {
pango_layout_set_font_description(layout, time2_font_desc);
pango_layout_set_indent(layout, 0);
pango_layout_set_text(layout, buf_date, strlen(buf_date));
pango_layout_set_width(layout, clock->area.width * PANGO_SCALE);
pango_cairo_update_layout(c, layout);
draw_text(layout, c, 0, clock->time2_posy, &clock->font, ((Panel *)clock->area.panel)->font_shadow);
}
g_object_unref(layout);
Clock *clock = (Clock *)obj;
Panel *panel = (Panel *)clock->area.panel;
draw_text_area(&clock->area,
c,
buf_time,
time2_format ? buf_date : NULL,
time1_font_desc,
time2_font_desc,
clock->time1_posy,
clock->time2_posy,
&clock->font,
panel->scale);
}
void clock_dump_geometry(void *obj, int indent)
{
Clock *clock = (Clock *)obj;
fprintf(stderr, "%*sText 1: y = %d, text = %s\n", indent, "", clock->time1_posy, buf_time);
fprintf(stderr, "tint2: %*sText 1: y = %d, text = %s\n", indent, "", clock->time1_posy, buf_time);
if (time2_format) {
fprintf(stderr, "%*sText 2: y = %d, text = %s\n", indent, "", clock->time2_posy, buf_date);
fprintf(stderr, "tint2: %*sText 2: y = %d, text = %s\n", indent, "", clock->time2_posy, buf_date);
}
}
@@ -393,7 +330,7 @@ char *clock_get_tooltip(void *obj)
return strdup(buf_tooltip);
}
void clock_action(int button, Time time)
void clock_action(void *obj, int button, int x, int y, Time time)
{
char *command = NULL;
switch (button) {
@@ -413,5 +350,5 @@ void clock_action(int button, Time time)
command = clock_dwheel_command;
break;
}
tint_exec(command, NULL, NULL, time);
tint_exec(command, NULL, NULL, time, obj, x, y, FALSE, TRUE);
}

View File

@@ -54,6 +54,6 @@ void draw_clock(void *obj, cairo_t *c);
gboolean resize_clock(void *obj);
void clock_action(int button, Time time);
void clock_action(void *obj, int button, int x, int y, Time time);
#endif

View File

@@ -119,7 +119,7 @@ void get_action(char *event, MouseAction *action)
else if (strcmp(event, "prev_task") == 0)
*action = PREV_TASK;
else
fprintf(stderr, "Error: unrecognized action '%s'. Please fix your config file.\n", event);
fprintf(stderr, "tint2: Error: unrecognized action '%s'. Please fix your config file.\n", event);
}
int get_task_status(char *status)
@@ -135,27 +135,36 @@ int get_task_status(char *status)
int config_get_monitor(char *monitor)
{
if (strcmp(monitor, "all") != 0) {
char *endptr;
int ret_int = strtol(monitor, &endptr, 10);
if (*endptr == 0)
return ret_int - 1;
else {
// monitor specified by name, not by index
int i, j;
for (i = 0; i < server.num_monitors; ++i) {
if (server.monitors[i].names == 0)
// xrandr can't identify monitors
continue;
j = 0;
while (server.monitors[i].names[j] != 0) {
if (strcmp(monitor, server.monitors[i].names[j++]) == 0)
return i;
}
if (strcmp(monitor, "primary") == 0) {
for (int i = 0; i < server.num_monitors; ++i) {
if (server.monitors[i].primary)
return i;
}
return 0;
}
if (strcmp(monitor, "all") == 0) {
return -1;
}
char *endptr;
int ret_int = strtol(monitor, &endptr, 10);
if (*endptr == 0)
return ret_int - 1;
else {
// monitor specified by name, not by index
int i, j;
for (i = 0; i < server.num_monitors; ++i) {
if (server.monitors[i].names == 0)
// xrandr can't identify monitors
continue;
j = 0;
while (server.monitors[i].names[j] != 0) {
if (strcmp(monitor, server.monitors[i].names[j++]) == 0)
return i;
}
}
}
// monitor == "all" or monitor not found or xrandr can't identify monitors
// monitor not found or xrandr can't identify monitors => all
return -1;
}
@@ -206,7 +215,7 @@ void load_launcher_app_dir(const char *path)
Separator *get_or_create_last_separator()
{
if (!panel_config.separator_list) {
fprintf(stderr, "Warning: separator items should shart with 'separator = new'\n");
fprintf(stderr, "tint2: Warning: separator items should shart with 'separator = new'\n");
panel_config.separator_list = g_list_append(panel_config.separator_list, create_separator());
}
return (Separator *)g_list_last(panel_config.separator_list)->data;
@@ -215,7 +224,7 @@ Separator *get_or_create_last_separator()
Execp *get_or_create_last_execp()
{
if (!panel_config.execp_list) {
fprintf(stderr, "Warning: execp items should start with 'execp = new'\n");
fprintf(stderr, "tint2: Warning: execp items should start with 'execp = new'\n");
panel_config.execp_list = g_list_append(panel_config.execp_list, create_execp());
}
return (Execp *)g_list_last(panel_config.execp_list)->data;
@@ -224,7 +233,7 @@ Execp *get_or_create_last_execp()
Button *get_or_create_last_button()
{
if (!panel_config.button_list) {
fprintf(stderr, "Warning: button items should start with 'button = new'\n");
fprintf(stderr, "tint2: Warning: button items should start with 'button = new'\n");
panel_config.button_list = g_list_append(panel_config.button_list, create_button());
}
return (Button *)g_list_last(panel_config.button_list)->data;
@@ -235,7 +244,11 @@ void add_entry(char *key, char *value)
char *value1 = 0, *value2 = 0, *value3 = 0;
/* Background and border */
if (strcmp(key, "rounded") == 0) {
if (strcmp(key, "scale_relative_to_dpi") == 0) {
ui_scale_dpi_ref = atof(value);
} else if (strcmp(key, "scale_relative_to_screen_height") == 0) {
ui_scale_monitor_size_ref = atof(value);
} else if (strcmp(key, "rounded") == 0) {
// 'rounded' is the first parameter => alloc a new background
if (backgrounds->len > 0) {
Background *bg = &g_array_index(backgrounds, Background, backgrounds->len - 1);
@@ -341,6 +354,12 @@ void add_entry(char *key, char *value)
id = (id < gradients->len && id >= 0) ? id : -1;
if (id >= 0)
bg->gradients[MOUSE_DOWN] = &g_array_index(gradients, GradientClass, id);
} else if (strcmp(key, "border_content_tint_weight") == 0) {
Background *bg = &g_array_index(backgrounds, Background, backgrounds->len - 1);
bg->border_content_tint_weight = MAX(0.0, MIN(1.0, atoi(value) / 100.));
} else if (strcmp(key, "background_content_tint_weight") == 0) {
Background *bg = &g_array_index(backgrounds, Background, backgrounds->len - 1);
bg->fill_content_tint_weight = MAX(0.0, MIN(1.0, atoi(value) / 100.));
}
/* Gradients */
@@ -381,8 +400,6 @@ void add_entry(char *key, char *value)
/* Panel */
else if (strcmp(key, "panel_monitor") == 0) {
panel_config.monitor = config_get_monitor(value);
} else if (strcmp(key, "primary_monitor_first") == 0) {
primary_monitor_first = atoi(value);
} else if (strcmp(key, "panel_shrink") == 0) {
panel_shrink = atoi(value);
} else if (strcmp(key, "panel_size") == 0) {
@@ -408,6 +425,7 @@ void add_entry(char *key, char *value)
}
} else if (strcmp(key, "panel_items") == 0) {
new_config_file = TRUE;
free_and_null(panel_items_order);
panel_items_order = strdup(value);
systray_enabled = 0;
launcher_enabled = 0;
@@ -425,7 +443,7 @@ void add_entry(char *key, char *value)
#ifdef ENABLE_BATTERY
battery_enabled = 1;
#else
fprintf(stderr, "tint2 is build without battery support\n");
fprintf(stderr, "tint2: tint2 has been compiled without battery support\n");
#endif
}
if (panel_items_order[j] == 'S') {
@@ -544,6 +562,11 @@ void add_entry(char *key, char *value)
#ifdef ENABLE_BATTERY
if (strlen(value) > 0)
battery_low_cmd = strdup(value);
#endif
} else if (strcmp(key, "battery_full_cmd") == 0) {
#ifdef ENABLE_BATTERY
if (strlen(value) > 0)
battery_full_cmd = strdup(value);
#endif
} else if (strcmp(key, "ac_connected_cmd") == 0) {
#ifdef ENABLE_BATTERY
@@ -564,6 +587,21 @@ void add_entry(char *key, char *value)
#ifdef ENABLE_BATTERY
bat2_font_desc = pango_font_description_from_string(value);
bat2_has_font = TRUE;
#endif
} else if (strcmp(key, "bat1_format") == 0) {
#ifdef ENABLE_BATTERY
if (strlen(value) > 0) {
free(bat1_format);
bat1_format = strdup(value);
battery_enabled = 1;
}
#endif
} else if (strcmp(key, "bat2_format") == 0) {
#ifdef ENABLE_BATTERY
if (strlen(value) > 0) {
free(bat2_format);
bat2_format = strdup(value);
}
#endif
} else if (strcmp(key, "battery_font_color") == 0) {
#ifdef ENABLE_BATTERY
@@ -626,7 +664,7 @@ void add_entry(char *key, char *value)
else if (g_str_equal(value, "dots"))
separator->style = SEPARATOR_DOTS;
else
fprintf(stderr, RED "Invalid separator_style value: %s" RESET "\n", value);
fprintf(stderr, RED "tint2: Invalid separator_style value: %s" RESET "\n", value);
} else if (strcmp(key, "separator_size") == 0) {
Separator *separator = get_or_create_last_separator();
separator->thickness = atoi(value);
@@ -652,8 +690,8 @@ void add_entry(char *key, char *value)
Execp *execp = get_or_create_last_execp();
execp->backend->interval = 0;
int v = atoi(value);
if (v < 1) {
fprintf(stderr, "execp_interval must be an integer >= 1\n");
if (v < 0) {
fprintf(stderr, "tint2: execp_interval must be an integer >= 0\n");
} else {
execp->backend->interval = v;
}
@@ -673,6 +711,7 @@ void add_entry(char *key, char *value)
Execp *execp = get_or_create_last_execp();
free_and_null(execp->backend->tooltip);
execp->backend->tooltip = strdup(value);
execp->backend->has_user_tooltip = TRUE;
} else if (strcmp(key, "execp_font") == 0) {
Execp *execp = get_or_create_last_execp();
pango_font_description_free(execp->backend->font_desc);
@@ -708,7 +747,7 @@ void add_entry(char *key, char *value)
Execp *execp = get_or_create_last_execp();
int v = atoi(value);
if (v < 0) {
fprintf(stderr, "execp_icon_w must be an integer >= 0\n");
fprintf(stderr, "tint2: execp_icon_w must be an integer >= 0\n");
} else {
execp->backend->icon_w = v;
}
@@ -716,7 +755,7 @@ void add_entry(char *key, char *value)
Execp *execp = get_or_create_last_execp();
int v = atoi(value);
if (v < 0) {
fprintf(stderr, "execp_icon_h must be an integer >= 0\n");
fprintf(stderr, "tint2: execp_icon_h must be an integer >= 0\n");
} else {
execp->backend->icon_h = v;
}
@@ -750,17 +789,23 @@ void add_entry(char *key, char *value)
/* Button */
else if (strcmp(key, "button") == 0) {
panel_config.button_list = g_list_append(panel_config.button_list, create_button());
} else if (strcmp(key, "button_icon") == 0 && strlen(value)) {
Button *button = get_or_create_last_button();
button->backend->icon_name = strdup(value);
} else if (strcmp(key, "button_text") == 0 && strlen(value)) {
Button *button = get_or_create_last_button();
free_and_null(button->backend->text);
button->backend->text = strdup(value);
} else if (strcmp(key, "button_tooltip") == 0 && strlen(value)) {
Button *button = get_or_create_last_button();
free_and_null(button->backend->tooltip);
button->backend->tooltip = strdup(value);
} else if (strcmp(key, "button_icon") == 0) {
if (strlen(value)) {
Button *button = get_or_create_last_button();
button->backend->icon_name = strdup(value);
}
} else if (strcmp(key, "button_text") == 0) {
if (strlen(value)) {
Button *button = get_or_create_last_button();
free_and_null(button->backend->text);
button->backend->text = strdup(value);
}
} else if (strcmp(key, "button_tooltip") == 0) {
if (strlen(value)) {
Button *button = get_or_create_last_button();
free_and_null(button->backend->tooltip);
button->backend->tooltip = strdup(value);
}
} else if (strcmp(key, "button_font") == 0) {
Button *button = get_or_create_last_button();
pango_font_description_free(button->backend->font_desc);
@@ -960,6 +1005,8 @@ void add_entry(char *key, char *value)
hide_inactive_tasks = atoi(value);
} else if (strcmp(key, "taskbar_hide_different_monitor") == 0) {
hide_task_diff_monitor = atoi(value);
} else if (strcmp(key, "taskbar_hide_different_desktop") == 0) {
hide_task_diff_desktop = atoi(value);
} else if (strcmp(key, "taskbar_hide_if_empty") == 0) {
hide_taskbar_if_empty = atoi(value);
} else if (strcmp(key, "taskbar_always_show_all_desktop_tasks") == 0) {
@@ -969,6 +1016,8 @@ void add_entry(char *key, char *value)
taskbar_sort_method = TASKBAR_SORT_CENTER;
} else if (strcmp(value, "title") == 0) {
taskbar_sort_method = TASKBAR_SORT_TITLE;
} else if (strcmp(value, "application") == 0) {
taskbar_sort_method = TASKBAR_SORT_APPLICATION;
} else if (strcmp(value, "lru") == 0) {
taskbar_sort_method = TASKBAR_SORT_LRU;
} else if (strcmp(value, "mru") == 0) {
@@ -1049,11 +1098,18 @@ void add_entry(char *key, char *value)
panel_config.g_task.config_background_mask |= (1 << status);
if (status == TASK_NORMAL)
panel_config.g_task.area.bg = panel_config.g_task.background[TASK_NORMAL];
if (panel_config.g_task.background[status]->border_content_tint_weight > 0 ||
panel_config.g_task.background[status]->fill_content_tint_weight > 0)
panel_config.g_task.has_content_tint = TRUE;
}
}
// "tooltip" is deprecated but here for backwards compatibility
else if (strcmp(key, "task_tooltip") == 0 || strcmp(key, "tooltip") == 0)
panel_config.g_task.tooltip_enabled = atoi(value);
else if (strcmp(key, "task_thumbnail") == 0)
panel_config.g_task.thumbnail_enabled = atoi(value);
else if (strcmp(key, "task_thumbnail_size") == 0)
panel_config.g_task.thumbnail_width = MAX(8, atoi(value));
/* Systray */
else if (strcmp(key, "systray_padding") == 0) {
@@ -1094,10 +1150,13 @@ void add_entry(char *key, char *value)
systray.saturation = atoi(value2);
systray.brightness = atoi(value3);
} else if (strcmp(key, "systray_monitor") == 0) {
systray_monitor = atoi(value) - 1;
systray_monitor = MAX(0, config_get_monitor(value));
} else if (strcmp(key, "systray_name_filter") == 0) {
if (systray_hide_name_filter)
if (systray_hide_name_filter) {
fprintf(stderr, "tint2: Error: duplicate option 'systray_name_filter'. Please use it only once. See "
"https://gitlab.com/o9000/tint2/issues/652\n");
free(systray_hide_name_filter);
}
systray_hide_name_filter = strdup(value);
}
@@ -1250,8 +1309,13 @@ void add_entry(char *key, char *value)
}
}
#endif
else
fprintf(stderr, "tint2 : invalid option \"%s\",\n upgrade tint2 or correct your config file\n", key);
else if (strcmp(key, "primary_monitor_first") == 0) {
fprintf(stderr,
"tint2: deprecated config option \"%s\"\n"
" Please see the documentation regarding the alternatives.\n",
key);
} else
fprintf(stderr, "tint2: invalid option \"%s\",\n upgrade tint2 or correct your config file\n", key);
if (value1)
free(value1);
@@ -1263,6 +1327,8 @@ void add_entry(char *key, char *value)
gboolean config_read_file(const char *path)
{
fprintf(stderr, "tint2: Loading config file: %s\n", path);
FILE *fp = fopen(path, "r");
if (!fp)
return FALSE;
@@ -1331,12 +1397,12 @@ gboolean config_read_default_path()
// copy tint2rc from system directory to user directory
fprintf(stderr, "tint2 warning: could not find a config file! Creating a default one.\n");
fprintf(stderr, "tint2: could not find a config file! Creating a default one.\n");
// According to the XDG Base Directory Specification
// (https://specifications.freedesktop.org/basedir-spec/basedir-spec-0.6.html)
// if the user's config directory does not exist, we should create it with permissions set to 0700.
if (!g_file_test(g_get_user_config_dir(), G_FILE_TEST_IS_DIR))
g_mkdir(g_get_user_config_dir(), 0700);
g_mkdir_with_parents(g_get_user_config_dir(), 0700);
gchar *path2 = 0;
system_dirs = g_get_system_config_dirs();
@@ -1353,7 +1419,7 @@ gboolean config_read_default_path()
// copy file in user directory (path1)
gchar *dir = g_build_filename(g_get_user_config_dir(), "tint2", NULL);
if (!g_file_test(dir, G_FILE_TEST_IS_DIR))
g_mkdir(dir, 0700);
g_mkdir_with_parents(dir, 0700);
g_free(dir);
path1 = g_build_filename(g_get_user_config_dir(), "tint2", "tint2rc", NULL);
@@ -1369,7 +1435,7 @@ gboolean config_read_default_path()
// generate config file
gchar *dir = g_build_filename(g_get_user_config_dir(), "tint2", NULL);
if (!g_file_test(dir, G_FILE_TEST_IS_DIR))
g_mkdir(dir, 0700);
g_mkdir_with_parents(dir, 0700);
g_free(dir);
path1 = g_build_filename(g_get_user_config_dir(), "tint2", "tint2rc", NULL);

5
src/default_icon.c Normal file

File diff suppressed because one or more lines are too long

10
src/default_icon.h Normal file
View File

@@ -0,0 +1,10 @@
#ifndef default_icon_h
#define default_icon_h
#include <Imlib2.h>
extern int default_icon_width;
extern int default_icon_height;
extern DATA32 default_icon_data[];
#endif

444
src/drag_and_drop.c Normal file
View File

@@ -0,0 +1,444 @@
/**************************************************************************
* Copyright (C) 2017 tint2 authors
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License version 2
* as published by the Free Software Foundation.
*
* This program 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 General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
**************************************************************************/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "drag_and_drop.h"
#include "panel.h"
#include "server.h"
#include "task.h"
// Drag and Drop state variables
static Window dnd_source_window;
static Window dnd_target_window;
static int dnd_version;
static Atom dnd_selection;
static Atom dnd_atom;
static int dnd_sent_request;
static LauncherIcon *dnd_launcher_icon;
gboolean debug_dnd = FALSE;
gboolean hidden_panel_shown_for_dnd;
// This fetches all the data from a property
struct Property dnd_read_property(Display *disp, Window w, Atom property)
{
Atom actual_type;
int actual_format;
unsigned long nitems;
unsigned long bytes_after;
unsigned char *ret = 0;
int read_bytes = 1024;
// Keep trying to read the property until there are no
// bytes unread.
do {
if (ret != 0)
XFree(ret);
XGetWindowProperty(disp,
w,
property,
0,
read_bytes,
False,
AnyPropertyType,
&actual_type,
&actual_format,
&nitems,
&bytes_after,
&ret);
read_bytes *= 2;
} while (bytes_after != 0);
if (debug_dnd)
fprintf(stderr, "tint2: DnD %s:%d: Property:\n", __FILE__, __LINE__);
fprintf(stderr, "tint2: DnD %s:%d: Actual type: %s\n", __FILE__, __LINE__, GetAtomName(disp, actual_type));
fprintf(stderr, "tint2: DnD %s:%d: Actual format: %d\n", __FILE__, __LINE__, actual_format);
fprintf(stderr, "tint2: DnD %s:%d: Number of items: %lu\n", __FILE__, __LINE__, nitems);
Property p;
p.data = ret;
p.format = actual_format;
p.nitems = nitems;
p.type = actual_type;
return p;
}
// This function takes a list of targets which can be converted to (atom_list, nitems)
// and a list of acceptable targets with prioritees (datatypes). It returns the highest
// entry in datatypes which is also in atom_list: ie it finds the best match.
Atom dnd_pick_target_from_list(Display *disp, Atom *atom_list, int nitems)
{
Atom to_be_requested = None;
int i;
for (i = 0; i < nitems; i++) {
const char *atom_name = GetAtomName(disp, atom_list[i]);
fprintf(stderr, "tint2: DnD %s:%d: Type %d = %s\n", __FILE__, __LINE__, i, atom_name);
// See if this data type is allowed and of higher priority (closer to zero)
// than the present one.
if (strcasecmp(atom_name, "STRING") == 0) {
to_be_requested = atom_list[i];
} else if (strcasecmp(atom_name, "text/uri-list") == 0 && !to_be_requested) {
to_be_requested = atom_list[i];
}
}
fprintf(stderr,
"tint2: DnD %s:%d: Accepting: Type %s\n",
__FILE__,
__LINE__,
GetAtomName(server.display, to_be_requested));
return to_be_requested;
}
// Finds the best target given up to three atoms provided (any can be None).
// Useful for part of the Xdnd protocol.
Atom dnd_pick_target_from_atoms(Display *disp, Atom t1, Atom t2, Atom t3)
{
Atom atoms[3];
int n = 0;
if (t1 != None)
atoms[n++] = t1;
if (t2 != None)
atoms[n++] = t2;
if (t3 != None)
atoms[n++] = t3;
return dnd_pick_target_from_list(disp, atoms, n);
}
// Finds the best target given a local copy of a property.
Atom dnd_pick_target_from_targets(Display *disp, Property p)
{
// The list of targets is a list of atoms, so it should have type XA_ATOM
// but it may have the type TARGETS instead.
if ((p.type != XA_ATOM && p.type != server.atom.TARGETS) || p.format != 32) {
// This would be really broken. Targets have to be an atom list
// and applications should support this. Nevertheless, some
// seem broken (MATLAB 7, for instance), so ask for STRING
// next instead as the lowest common denominator
return XA_STRING;
} else {
Atom *atom_list = (Atom *)p.data;
return dnd_pick_target_from_list(disp, atom_list, p.nitems);
}
}
void dnd_init()
{
dnd_source_window = 0;
dnd_target_window = 0;
dnd_version = 0;
dnd_selection = XInternAtom(server.display, "PRIMARY", 0);
dnd_atom = None;
dnd_sent_request = 0;
dnd_launcher_icon = NULL;
hidden_panel_shown_for_dnd = FALSE;
}
void handle_dnd_enter(XClientMessageEvent *e)
{
dnd_atom = None;
int more_than_3 = e->data.l[1] & 1;
dnd_source_window = e->data.l[0];
dnd_version = (e->data.l[1] >> 24);
if (debug_dnd) {
fprintf(stderr, "tint2: DnD %s:%d: DnDEnter\n", __FILE__, __LINE__);
fprintf(stderr,
"DnD %s:%d: DnDEnter. Supports > 3 types = %s\n",
__FILE__,
__LINE__,
more_than_3 ? "yes" : "no");
fprintf(stderr, "tint2: DnD %s:%d: Protocol version = %d\n", __FILE__, __LINE__, dnd_version);
fprintf(stderr,
"tint2: DnD %s:%d: Type 1 = %s\n",
__FILE__,
__LINE__,
GetAtomName(server.display, e->data.l[2]));
fprintf(stderr,
"tint2: DnD %s:%d: Type 2 = %s\n",
__FILE__,
__LINE__,
GetAtomName(server.display, e->data.l[3]));
fprintf(stderr,
"tint2: DnD %s:%d: Type 3 = %s\n",
__FILE__,
__LINE__,
GetAtomName(server.display, e->data.l[4]));
}
// Query which conversions are available and pick the best
if (more_than_3) {
// Fetch the list of possible conversions
// Notice the similarity to TARGETS with paste.
Property p = dnd_read_property(server.display, dnd_source_window, server.atom.XdndTypeList);
dnd_atom = dnd_pick_target_from_targets(server.display, p);
XFree(p.data);
} else {
// Use the available list
dnd_atom = dnd_pick_target_from_atoms(server.display, e->data.l[2], e->data.l[3], e->data.l[4]);
}
if (debug_dnd)
fprintf(stderr,
"tint2: DnD %s:%d: Requested type = %s\n",
__FILE__,
__LINE__,
GetAtomName(server.display, dnd_atom));
}
void handle_dnd_position(XClientMessageEvent *e)
{
dnd_target_window = e->window;
int accept = 0;
Panel *panel = get_panel(e->window);
int x, y, mapX, mapY;
Window child;
x = (e->data.l[2] >> 16) & 0xFFFF;
y = e->data.l[2] & 0xFFFF;
XTranslateCoordinates(server.display, server.root_win, e->window, x, y, &mapX, &mapY, &child);
Task *task = click_task(panel, mapX, mapY);
if (task) {
if (task->desktop != server.desktop)
change_desktop(task->desktop);
task_handle_mouse_event(task, TOGGLE);
} else {
LauncherIcon *icon = click_launcher_icon(panel, mapX, mapY);
if (icon) {
accept = 1;
dnd_launcher_icon = icon;
} else {
dnd_launcher_icon = NULL;
}
}
// send XdndStatus event to get more XdndPosition events
XClientMessageEvent se;
se.type = ClientMessage;
se.window = e->data.l[0];
se.message_type = server.atom.XdndStatus;
se.format = 32;
se.data.l[0] = e->window; // XID of the target window
se.data.l[1] = accept ? 1 : 0; // bit 0: accept drop bit 1: send XdndPosition events if inside rectangle
se.data.l[2] = 0; // Rectangle x,y for which no more XdndPosition events
se.data.l[3] = (1 << 16) | 1; // Rectangle w,h for which no more XdndPosition events
if (accept) {
se.data.l[4] = server.atom.XdndActionCopy;
} else {
se.data.l[4] = None; // None = drop will not be accepted
}
if (debug_dnd)
fprintf(stderr,
"tint2: DnD %s:%d: Accepted: %s\n",
__FILE__,
__LINE__,
accept ? GetAtomName(server.display, (Atom)se.data.l[4]) : "no");
XSendEvent(server.display, e->data.l[0], False, NoEventMask, (XEvent *)&se);
}
void handle_dnd_drop(XClientMessageEvent *e)
{
if (dnd_target_window && dnd_launcher_icon) {
if (dnd_version >= 1) {
XConvertSelection(server.display,
server.atom.XdndSelection,
dnd_atom,
dnd_selection,
dnd_target_window,
e->data.l[2]);
} else {
XConvertSelection(server.display,
server.atom.XdndSelection,
dnd_atom,
dnd_selection,
dnd_target_window,
CurrentTime);
}
} else {
// The source is sending anyway, despite instructions to the contrary.
// So reply that we're not interested.
XClientMessageEvent m;
memset(&m, 0, sizeof(m));
m.type = ClientMessage;
m.display = e->display;
m.window = e->data.l[0];
m.message_type = server.atom.XdndFinished;
m.format = 32;
m.data.l[0] = dnd_target_window;
m.data.l[1] = 0;
m.data.l[2] = None; // Failed.
XSendEvent(server.display, e->data.l[0], False, NoEventMask, (XEvent *)&m);
}
}
void handle_dnd_selection_notify(XSelectionEvent *e)
{
Atom target = e->target;
if (debug_dnd) {
fprintf(stderr, "tint2: DnD %s:%d: A selection notify has arrived!\n", __FILE__, __LINE__);
fprintf(stderr,
"DnD %s:%d: Selection atom = %s\n",
__FILE__,
__LINE__,
GetAtomName(server.display, e->selection));
fprintf(stderr, "tint2: DnD %s:%d: Target atom = %s\n", __FILE__, __LINE__, GetAtomName(server.display, target));
fprintf(stderr,
"DnD %s:%d: Property atom = %s\n",
__FILE__,
__LINE__,
GetAtomName(server.display, e->property));
}
if (dnd_launcher_icon) {
Property prop = dnd_read_property(server.display, dnd_target_window, dnd_selection);
if (prop.data) {
// If we're being given a list of targets (possible conversions)
if (target == server.atom.TARGETS && !dnd_sent_request) {
dnd_sent_request = 1;
dnd_atom = dnd_pick_target_from_targets(server.display, prop);
if (dnd_atom == None) {
if (debug_dnd)
fprintf(stderr, "tint2: No matching datatypes.\n");
} else {
// Request the data type we are able to select
if (debug_dnd)
fprintf(stderr, "tint2: Now requsting type %s", GetAtomName(server.display, dnd_atom));
XConvertSelection(server.display,
dnd_selection,
dnd_atom,
dnd_selection,
dnd_target_window,
CurrentTime);
}
} else if (target == dnd_atom) {
// Dump the binary data
if (debug_dnd) {
fprintf(stderr, "tint2: DnD %s:%d: Received data:\n", __FILE__, __LINE__);
fprintf(stderr, "tint2: --------\n");
for (int i = 0; i < prop.nitems * prop.format / 8; i++)
fprintf(stderr, "%c", ((char *)prop.data)[i]);
fprintf(stderr, "tint2: --------\n");
}
// https://standards.freedesktop.org/desktop-entry-spec/desktop-entry-spec-latest.html#exec-variables
GString *cmd = g_string_new(dnd_launcher_icon->cmd);
const char *atom_name = GetAtomName(server.display, prop.type);
if (strcasecmp(atom_name, "STRING") == 0 || strcasecmp(atom_name, "text/uri-list") == 0) {
GString *url = g_string_new("");
GString *prev_url = g_string_new("");
gboolean must_unescape = strcasecmp(atom_name, "text/uri-list") == 0;
for (int i = 0; i < prop.nitems * prop.format / 8; i++) {
char c = ((char *)prop.data)[i];
if (c == '\n') {
if (must_unescape) {
char *raw = g_uri_unescape_string(url->str, NULL);
if (raw) {
g_string_assign(url, raw);
}
free(raw);
}
// Many programs cannot handle this prefix
tint2_g_string_replace(url, "file://", "");
// Some programs put duplicates in the list, we remove them
if (strcmp(url->str, prev_url->str) != 0) {
if (strstr(cmd->str, "%F")) {
GString *piece = g_string_new("");
g_string_append(piece, " \"");
g_string_append(piece, url->str);
g_string_append(piece, "\"");
g_string_append(piece, " %F");
tint2_g_string_replace(cmd, "%F", piece->str);
g_string_free(piece, TRUE);
} else if (strstr(cmd->str, "%f")) {
GString *piece = g_string_new("");
g_string_append(piece, " \"");
g_string_append(piece, url->str);
g_string_append(piece, "\"");
tint2_g_string_replace(cmd, "%f", piece->str);
g_string_free(piece, TRUE);
break;
} else {
g_string_append(cmd, " \"");
g_string_append(cmd, url->str);
g_string_append(cmd, "\"");
}
}
g_string_assign(prev_url, url->str);
g_string_assign(url, "");
} else if (c == '\r') {
// Nothing to do
} else {
if (c == '`' || c == '$' || c == '\\') {
g_string_append(url, "\\");
}
g_string_append_c(url, c);
}
}
g_string_free(url, TRUE);
g_string_free(prev_url, TRUE);
}
tint2_g_string_replace(cmd, "%F", "");
tint2_g_string_replace(cmd, "%f", "");
if (debug_dnd)
fprintf(stderr, "tint2: DnD %s:%d: Running command: %s\n", __FILE__, __LINE__, cmd->str);
tint_exec(cmd->str,
NULL,
NULL,
e->time,
NULL,
0,
0,
dnd_launcher_icon->start_in_terminal,
dnd_launcher_icon->startup_notification);
g_string_free(cmd, TRUE);
// Reply OK.
XClientMessageEvent m;
memset(&m, 0, sizeof(m));
m.type = ClientMessage;
m.display = server.display;
m.window = dnd_source_window;
m.message_type = server.atom.XdndFinished;
m.format = 32;
m.data.l[0] = dnd_target_window;
m.data.l[1] = 1;
m.data.l[2] = server.atom.XdndActionCopy; // We only ever copy.
XSendEvent(server.display, dnd_source_window, False, NoEventMask, (XEvent *)&m);
XSync(server.display, False);
}
XFree(prop.data);
}
}
}

22
src/drag_and_drop.h Normal file
View File

@@ -0,0 +1,22 @@
/**************************************************************************
* Copyright (C) 2017 tint2 authors
*
**************************************************************************/
#ifndef DRAG_AND_DROP_H
#define DRAG_AND_DROP_H
#include <X11/Xlib.h>
#include <glib.h>
extern gboolean hidden_panel_shown_for_dnd;
extern gboolean debug_dnd;
void dnd_init();
void handle_dnd_enter(XClientMessageEvent *e);
void handle_dnd_position(XClientMessageEvent *e);
void handle_dnd_drop(XClientMessageEvent *e);
void handle_dnd_selection_notify(XSelectionEvent *e);
#endif

View File

@@ -18,6 +18,10 @@
#include "timer.h"
#include "common.h"
#define MAX_TOOLTIP_LEN 4096
bool debug_executors = false;
void execp_timer_callback(void *arg);
char *execp_get_tooltip(void *obj);
void execp_init_fonts();
@@ -30,14 +34,16 @@ void default_execp()
Execp *create_execp()
{
Execp *execp = calloc(1, sizeof(Execp));
execp->backend = calloc(1, sizeof(ExecpBackend));
execp->backend->child_pipe = -1;
Execp *execp = (Execp *)calloc(1, sizeof(Execp));
execp->backend = (ExecpBackend *)calloc(1, sizeof(ExecpBackend));
execp->backend->child_pipe_stdout = -1;
execp->backend->child_pipe_stderr = -1;
execp->backend->cmd_pids = g_tree_new(cmp_ptr);
execp->backend->interval = 30;
execp->backend->cache_icon = TRUE;
execp->backend->centered = TRUE;
execp->backend->font_color.alpha = 0.5;
INIT_TIMER(execp->backend->timer);
return execp;
}
@@ -45,10 +51,10 @@ gpointer create_execp_frontend(gconstpointer arg, gpointer data)
{
Execp *execp_backend = (Execp *)arg;
Execp *execp_frontend = calloc(1, sizeof(Execp));
Execp *execp_frontend = (Execp *)calloc(1, sizeof(Execp));
execp_frontend->backend = execp_backend->backend;
execp_backend->backend->instances = g_list_append(execp_backend->backend->instances, execp_frontend);
execp_frontend->frontend = calloc(1, sizeof(ExecpFrontend));
execp_frontend->frontend = (ExecpFrontend *)calloc(1, sizeof(ExecpFrontend));
return execp_frontend;
}
@@ -64,24 +70,24 @@ void destroy_execp(void *obj)
free_and_null(execp);
} else {
// This is a backend element
stop_timeout(execp->backend->timer);
execp->backend->timer = NULL;
destroy_timer(&execp->backend->timer);
if (execp->backend->icon) {
imlib_context_set_image(execp->backend->icon);
imlib_free_image();
execp->backend->icon = NULL;
}
free_and_null(execp->backend->buf_output);
free_icon(execp->backend->icon);
free_and_null(execp->backend->buf_stdout);
free_and_null(execp->backend->buf_stderr);
free_and_null(execp->backend->text);
free_and_null(execp->backend->icon_path);
if (execp->backend->child) {
kill(-execp->backend->child, SIGHUP);
execp->backend->child = 0;
}
if (execp->backend->child_pipe >= 0) {
close(execp->backend->child_pipe);
execp->backend->child_pipe = -1;
if (execp->backend->child_pipe_stdout >= 0) {
close(execp->backend->child_pipe_stdout);
execp->backend->child_pipe_stdout = -1;
}
if (execp->backend->child_pipe_stderr >= 0) {
close(execp->backend->child_pipe_stderr);
execp->backend->child_pipe_stderr = -1;
}
if (execp->backend->cmd_pids) {
g_tree_destroy(execp->backend->cmd_pids);
@@ -100,8 +106,8 @@ void destroy_execp(void *obj)
free_and_null(execp->backend->uwheel_command);
if (execp->backend->instances) {
fprintf(stderr, "Error: Attempt to destroy backend while there are still frontend instances!\n");
exit(-1);
fprintf(stderr, "tint2: Error: Attempt to destroy backend while there are still frontend instances!\n");
exit(EXIT_FAILURE);
}
free(execp->backend);
free(execp);
@@ -138,9 +144,11 @@ void init_execp()
// Set missing config options
if (!execp->backend->bg)
execp->backend->bg = &g_array_index(backgrounds, Background, 0);
execp->backend->buf_capacity = 1024;
execp->backend->buf_output = calloc(execp->backend->buf_capacity, 1);
execp->backend->text = strdup(" ");
execp->backend->buf_stdout_capacity = 1024;
execp->backend->buf_stdout = calloc(execp->backend->buf_stdout_capacity, 1);
execp->backend->buf_stderr_capacity = 1024;
execp->backend->buf_stderr = calloc(execp->backend->buf_stderr_capacity, 1);
execp->backend->text = strdup("");
execp->backend->icon_path = NULL;
}
}
@@ -186,8 +194,9 @@ void init_execp_panel(void *p)
execp->area.on_screen = TRUE;
instantiate_area_gradients(&execp->area);
if (!execp->backend->timer)
execp->backend->timer = add_timeout(10, 0, execp_timer_callback, execp, &execp->backend->timer);
change_timer(&execp->backend->timer, true, 10, 0, execp_timer_callback, execp);
execp_update_post_read(execp);
}
}
@@ -290,156 +299,138 @@ gboolean reload_icon(Execp *execp)
return FALSE;
}
int execp_compute_desired_size(void *obj)
void execp_compute_icon_text_geometry(Execp *execp,
int *horiz_padding,
int *vert_padding,
int *interior_padding,
int *icon_w,
int *icon_h,
gboolean *text_next_line,
int *txt_height,
int *txt_width,
int *new_size,
gboolean *resized)
{
Execp *execp = (Execp *)obj;
Panel *panel = (Panel *)execp->area.panel;
int horiz_padding = (panel_horizontal ? execp->area.paddingxlr : execp->area.paddingy);
int vert_padding = (panel_horizontal ? execp->area.paddingy : execp->area.paddingxlr);
int interior_padding = execp->area.paddingx;
Area *area = &execp->area;
*horiz_padding = (panel_horizontal ? area->paddingxlr : area->paddingy) * panel->scale;
*vert_padding = (panel_horizontal ? area->paddingy : area->paddingxlr) * panel->scale;
*interior_padding = area->paddingx * panel->scale;
int icon_w, icon_h;
if (reload_icon(execp)) {
if (execp->backend->icon) {
imlib_context_set_image(execp->backend->icon);
icon_w = imlib_image_get_width();
icon_h = imlib_image_get_height();
*icon_w = imlib_image_get_width();
*icon_h = imlib_image_get_height();
} else {
icon_w = icon_h = 0;
*icon_w = *icon_h = 0;
}
} else {
icon_w = icon_h = 0;
*icon_w = *icon_h = 0;
}
int text_next_line = !panel_horizontal && icon_w > execp->area.width / 2;
*text_next_line = !panel_horizontal && *icon_w > area->width / 2;
int txt_height_ink, txt_height, txt_width;
int available_w, available_h;
if (panel_horizontal) {
get_text_size2(execp->backend->font_desc,
&txt_height_ink,
&txt_height,
&txt_width,
panel->area.height,
panel->area.width,
execp->backend->text,
strlen(execp->backend->text),
PANGO_WRAP_WORD_CHAR,
PANGO_ELLIPSIZE_NONE,
execp->backend->has_markup);
available_w = panel->area.width;
available_h = area->height - 2 * *vert_padding - left_right_border_width(area);
} else {
get_text_size2(execp->backend->font_desc,
&txt_height_ink,
&txt_height,
&txt_width,
panel->area.height,
!text_next_line
? execp->area.width - icon_w - (icon_w ? interior_padding : 0) - 2 * horiz_padding -
left_right_border_width(&execp->area)
: execp->area.width - 2 * horiz_padding - left_right_border_width(&execp->area),
execp->backend->text,
strlen(execp->backend->text),
PANGO_WRAP_WORD_CHAR,
PANGO_ELLIPSIZE_NONE,
execp->backend->has_markup);
available_w = !text_next_line
? area->width - *icon_w - (*icon_w ? *interior_padding : 0) - 2 * *horiz_padding -
left_right_border_width(area)
: area->width - 2 * *horiz_padding - left_right_border_width(area);
available_h = panel->area.height;
}
get_text_size2(execp->backend->font_desc,
txt_height,
txt_width,
available_h,
available_w,
execp->backend->text,
strlen(execp->backend->text),
PANGO_WRAP_WORD_CHAR,
PANGO_ELLIPSIZE_NONE,
execp->backend->centered ? PANGO_ALIGN_CENTER : PANGO_ALIGN_LEFT,
execp->backend->has_markup,
panel->scale);
*resized = FALSE;
if (panel_horizontal) {
int new_size = txt_width;
if (icon_w)
new_size += interior_padding + icon_w;
new_size += 2 * horiz_padding + left_right_border_width(&execp->area);
return new_size;
} else {
int new_size;
if (!text_next_line) {
new_size = txt_height + 2 * vert_padding + top_bottom_border_width(&execp->area);
new_size = MAX(new_size, icon_h + 2 * vert_padding + top_bottom_border_width(&execp->area));
*new_size = *txt_width;
if (*icon_w)
*new_size += *interior_padding + *icon_w;
*new_size += 2 * *horiz_padding + left_right_border_width(area);
if (*new_size < area->width && abs(*new_size - area->width) < 6) {
// we try to limit the number of resizes
*new_size = area->width;
*resized = TRUE;
} else {
new_size =
icon_h + interior_padding + txt_height + 2 * vert_padding + top_bottom_border_width(&execp->area);
*resized = *new_size != area->width;
}
} else {
if (!*text_next_line) {
*new_size = *txt_height + 2 * *vert_padding + top_bottom_border_width(area);
*new_size = MAX(*new_size, *icon_h + 2 * *vert_padding + top_bottom_border_width(area));
} else {
*new_size = *icon_h + *interior_padding + *txt_height + 2 * *vert_padding + top_bottom_border_width(area);
}
if (*new_size != area->height) {
*resized = TRUE;
}
return new_size;
}
}
int execp_compute_desired_size(void *obj)
{
Execp *execp = (Execp *)obj;
int horiz_padding, vert_padding, interior_padding;
int icon_w, icon_h;
gboolean text_next_line;
int txt_height, txt_width;
int new_size;
gboolean resized;
execp_compute_icon_text_geometry(execp,
&horiz_padding,
&vert_padding,
&interior_padding,
&icon_w,
&icon_h,
&text_next_line,
&txt_height,
&txt_width,
&new_size,
&resized);
return new_size;
}
gboolean resize_execp(void *obj)
{
Execp *execp = (Execp *)obj;
Panel *panel = (Panel *)execp->area.panel;
int horiz_padding = (panel_horizontal ? execp->area.paddingxlr : execp->area.paddingy);
int vert_padding = (panel_horizontal ? execp->area.paddingy : execp->area.paddingxlr);
int interior_padding = execp->area.paddingx;
int horiz_padding, vert_padding, interior_padding;
int icon_w, icon_h;
if (reload_icon(execp)) {
if (execp->backend->icon) {
imlib_context_set_image(execp->backend->icon);
icon_w = imlib_image_get_width();
icon_h = imlib_image_get_height();
} else {
icon_w = icon_h = 0;
}
} else {
icon_w = icon_h = 0;
}
gboolean text_next_line;
int txt_height, txt_width;
int new_size;
gboolean resized;
execp_compute_icon_text_geometry(execp,
&horiz_padding,
&vert_padding,
&interior_padding,
&icon_w,
&icon_h,
&text_next_line,
&txt_height,
&txt_width,
&new_size,
&resized);
int text_next_line = !panel_horizontal && icon_w > execp->area.width / 2;
if (panel_horizontal)
execp->area.width = new_size;
else
execp->area.height = new_size;
int txt_height_ink, txt_height, txt_width;
if (panel_horizontal) {
get_text_size2(execp->backend->font_desc,
&txt_height_ink,
&txt_height,
&txt_width,
panel->area.height,
panel->area.width,
execp->backend->text,
strlen(execp->backend->text),
PANGO_WRAP_WORD_CHAR,
PANGO_ELLIPSIZE_NONE,
execp->backend->has_markup);
} else {
get_text_size2(execp->backend->font_desc,
&txt_height_ink,
&txt_height,
&txt_width,
panel->area.height,
!text_next_line
? execp->area.width - icon_w - (icon_w ? interior_padding : 0) - 2 * horiz_padding -
left_right_border_width(&execp->area)
: execp->area.width - 2 * horiz_padding - left_right_border_width(&execp->area),
execp->backend->text,
strlen(execp->backend->text),
PANGO_WRAP_WORD_CHAR,
PANGO_ELLIPSIZE_NONE,
execp->backend->has_markup);
}
gboolean result = FALSE;
if (panel_horizontal) {
int new_size = txt_width;
if (icon_w)
new_size += interior_padding + icon_w;
new_size += 2 * horiz_padding + left_right_border_width(&execp->area);
if (new_size > execp->area.width || new_size < (execp->area.width - 6)) {
// we try to limit the number of resize
execp->area.width = new_size + 1;
result = TRUE;
}
} else {
int new_size;
if (!text_next_line) {
new_size = txt_height + 2 * vert_padding + top_bottom_border_width(&execp->area);
new_size = MAX(new_size, icon_h + 2 * vert_padding + top_bottom_border_width(&execp->area));
} else {
new_size =
icon_h + interior_padding + txt_height + 2 * vert_padding + top_bottom_border_width(&execp->area);
}
if (new_size != execp->area.height) {
execp->area.height = new_size;
result = TRUE;
}
}
execp->frontend->textw = txt_width;
execp->frontend->texth = txt_height;
if (execp->backend->centered) {
@@ -479,14 +470,17 @@ gboolean resize_execp(void *obj)
}
schedule_redraw(&execp->area);
return result;
return resized;
}
void draw_execp(void *obj, cairo_t *c)
{
Execp *execp = obj;
PangoLayout *layout = pango_cairo_create_layout(c);
Execp *execp = (Execp *)obj;
Panel *panel = (Panel *)execp->area.panel;
PangoContext *context = pango_cairo_create_context(c);
pango_cairo_context_set_resolution(context, 96 * panel->scale);
PangoLayout *layout = pango_layout_new(context);
if (execp->backend->has_icon && execp->backend->icon) {
imlib_context_set_image(execp->backend->icon);
@@ -496,7 +490,8 @@ void draw_execp(void *obj, cairo_t *c)
// draw layout
pango_layout_set_font_description(layout, execp->backend->font_desc);
pango_layout_set_width(layout, execp->frontend->textw * PANGO_SCALE);
pango_layout_set_width(layout, (execp->frontend->textw + TINT2_PANGO_SLACK) * PANGO_SCALE);
pango_layout_set_height(layout, (execp->frontend->texth + TINT2_PANGO_SLACK) * PANGO_SCALE);
pango_layout_set_alignment(layout, execp->backend->centered ? PANGO_ALIGN_CENTER : PANGO_ALIGN_LEFT);
pango_layout_set_wrap(layout, PANGO_WRAP_WORD_CHAR);
pango_layout_set_ellipsize(layout, PANGO_ELLIPSIZE_NONE);
@@ -514,17 +509,18 @@ void draw_execp(void *obj, cairo_t *c)
panel_config.font_shadow);
g_object_unref(layout);
g_object_unref(context);
}
void execp_dump_geometry(void *obj, int indent)
{
Execp *execp = obj;
Execp *execp = (Execp *)obj;
if (execp->backend->has_icon && execp->backend->icon) {
Imlib_Image tmp = imlib_context_get_image();
imlib_context_set_image(execp->backend->icon);
fprintf(stderr,
"%*sIcon: x = %d, y = %d, w = %d, h = %d\n",
"tint2: %*sIcon: x = %d, y = %d, w = %d, h = %d\n",
indent,
"",
execp->frontend->iconx,
@@ -535,7 +531,7 @@ void execp_dump_geometry(void *obj, int indent)
imlib_context_set_image(tmp);
}
fprintf(stderr,
"%*sText: x = %d, y = %d, w = %d, align = %s, text = %s\n",
"tint2: %*sText: x = %d, y = %d, w = %d, align = %s, text = %s\n",
indent,
"",
execp->frontend->textx,
@@ -547,19 +543,17 @@ void execp_dump_geometry(void *obj, int indent)
void execp_force_update(Execp *execp)
{
if (execp->backend->child_pipe > 0) {
if (execp->backend->child_pipe_stdout > 0) {
// Command currently running, nothing to do
} else {
if (execp->backend->timer)
stop_timeout(execp->backend->timer);
// Run command right away
execp->backend->timer = add_timeout(10, 0, execp_timer_callback, execp, &execp->backend->timer);
change_timer(&execp->backend->timer, true, 10, 0, execp_timer_callback, execp);
}
}
void execp_action(void *obj, int button, int x, int y, Time time)
{
Execp *execp = obj;
Execp *execp = (Execp *)obj;
char *command = NULL;
switch (button) {
case 1:
@@ -579,17 +573,17 @@ void execp_action(void *obj, int button, int x, int y, Time time)
break;
}
if (command) {
char *full_cmd = g_strdup_printf("export EXECP_X=%d;"
"export EXECP_Y=%d;"
"export EXECP_W=%d;"
"export EXECP_H=%d; %s",
x,
y,
execp->area.width,
execp->area.height,
command);
tint_exec(full_cmd, NULL, NULL, time);
g_free(full_cmd);
setenvd("EXECP_X", x);
setenvd("EXECP_Y", y);
setenvd("EXECP_W", execp->area.width);
setenvd("EXECP_H", execp->area.height);
pid_t pid = tint_exec(command, NULL, NULL, time, obj, x, y, FALSE, TRUE);
unsetenv("EXECP_X");
unsetenv("EXECP_Y");
unsetenv("EXECP_W");
unsetenv("EXECP_H");
if (pid > 0)
g_tree_insert(execp->backend->cmd_pids, GINT_TO_POINTER(pid), GINT_TO_POINTER(1));
} else {
execp_force_update(execp);
}
@@ -603,78 +597,94 @@ void execp_cmd_completed(Execp *execp, pid_t pid)
void execp_timer_callback(void *arg)
{
Execp *execp = arg;
Execp *execp = (Execp *)arg;
if (!execp->backend->command)
return;
// Still running!
if (execp->backend->child_pipe > 0)
if (execp->backend->child_pipe_stdout > 0)
return;
int pipe_fd[2];
if (pipe(pipe_fd)) {
int pipe_fd_stdout[2];
if (pipe(pipe_fd_stdout)) {
// TODO maybe write this in tooltip, but if this happens we're screwed anyways
fprintf(stderr, "Execp: Creating pipe failed!\n");
fprintf(stderr, "tint2: Execp: Creating pipe failed!\n");
return;
}
fcntl(pipe_fd[0], F_SETFL, O_NONBLOCK | fcntl(pipe_fd[0], F_GETFL));
fcntl(pipe_fd_stdout[0], F_SETFL, O_NONBLOCK | fcntl(pipe_fd_stdout[0], F_GETFL));
int pipe_fd_stderr[2];
if (pipe(pipe_fd_stderr)) {
close(pipe_fd_stdout[1]);
close(pipe_fd_stdout[0]);
// TODO maybe write this in tooltip, but if this happens we're screwed anyways
fprintf(stderr, "tint2: Execp: Creating pipe failed!\n");
return;
}
fcntl(pipe_fd_stderr[0], F_SETFL, O_NONBLOCK | fcntl(pipe_fd_stderr[0], F_GETFL));
// Fork and run command, capturing stdout in pipe
pid_t child = fork();
if (child == -1) {
// TODO maybe write this in tooltip, but if this happens we're screwed anyways
fprintf(stderr, "Fork failed.\n");
close(pipe_fd[1]);
close(pipe_fd[0]);
fprintf(stderr, "tint2: Fork failed.\n");
close(pipe_fd_stdout[1]);
close(pipe_fd_stdout[0]);
close(pipe_fd_stderr[1]);
close(pipe_fd_stderr[0]);
return;
} else if (child == 0) {
fprintf(stderr, "Executing: %s\n", execp->backend->command);
if (debug_executors)
fprintf(stderr, "tint2: Executing: %s\n", execp->backend->command);
// We are in the child
close(pipe_fd[0]);
dup2(pipe_fd[1], 1); // 1 is stdout
close(pipe_fd[1]);
close(pipe_fd_stdout[0]);
dup2(pipe_fd_stdout[1], 1); // 1 is stdout
close(pipe_fd_stdout[1]);
close(pipe_fd_stderr[0]);
dup2(pipe_fd_stderr[1], 2); // 2 is stderr
close(pipe_fd_stderr[1]);
close_all_fds();
setpgid(0, 0);
execl("/bin/sh", "/bin/sh", "-c", execp->backend->command, NULL);
// This should never happen!
fprintf(stdout, "execl() failed\nexecl() failed\n");
fflush(stdout);
fprintf(stderr, "execl() failed\nexecl() failed\n");
exit(0);
}
close(pipe_fd[1]);
close(pipe_fd_stdout[1]);
close(pipe_fd_stderr[1]);
execp->backend->child = child;
execp->backend->child_pipe = pipe_fd[0];
execp->backend->buf_length = 0;
execp->backend->buf_output[execp->backend->buf_length] = '\0';
execp->backend->child_pipe_stdout = pipe_fd_stdout[0];
execp->backend->child_pipe_stderr = pipe_fd_stderr[0];
execp->backend->buf_stdout_length = 0;
execp->backend->buf_stdout[execp->backend->buf_stdout_length] = '\0';
execp->backend->buf_stderr_length = 0;
execp->backend->buf_stderr[execp->backend->buf_stderr_length] = '\0';
execp->backend->last_update_start_time = time(NULL);
}
gboolean read_execp(void *obj)
void read_from_pipe(int fd, char **buffer, ssize_t *buffer_length, ssize_t *buffer_capacity, gboolean *eof)
{
Execp *execp = (Execp *)obj;
if (execp->backend->child_pipe < 0)
return FALSE;
gboolean command_finished = FALSE;
*eof = FALSE;
while (1) {
// Make sure there is free space in the buffer
if (execp->backend->buf_capacity - execp->backend->buf_length < 1024) {
execp->backend->buf_capacity *= 2;
execp->backend->buf_output = realloc(execp->backend->buf_output, execp->backend->buf_capacity);
if (*buffer_capacity - *buffer_length < 1024) {
*buffer_capacity *= 2;
*buffer = (char *)realloc(*buffer, *buffer_capacity);
}
ssize_t count = read(execp->backend->child_pipe,
execp->backend->buf_output + execp->backend->buf_length,
execp->backend->buf_capacity - execp->backend->buf_length - 1);
ssize_t count = read(fd,
*buffer + *buffer_length,
*buffer_capacity - *buffer_length - 1);
if (count > 0) {
// Successful read
execp->backend->buf_length += count;
execp->backend->buf_output[execp->backend->buf_length] = '\0';
*buffer_length += count;
(*buffer)[*buffer_length] = '\0';
continue;
} else if (count == 0) {
// End of file
command_finished = TRUE;
*eof = TRUE;
break;
} else if (errno == EAGAIN || errno == EWOULDBLOCK) {
// No more data available at the moment
@@ -684,28 +694,86 @@ gboolean read_execp(void *obj)
continue;
} else {
// Error
command_finished = TRUE;
*eof = TRUE;
break;
}
break;
}
}
gboolean starts_with(char *s, char *prefix)
{
char *p, *q;
for (p = s, q = prefix; *p && *q; p++, q++) {
if (*p != *q)
return FALSE;
}
return *q == '\0';
}
char *last_substring(char *s, char *sub)
{
char *result = NULL;
for (char *p = s; *p; p++) {
if (starts_with(p, sub))
result = p;
}
return result;
}
void rstrip(char *s)
{
size_t len = strlen(s);
while (len > 0) {
if (s[len-1] == ' ' || s[len-1] == '\n') {
s[len-1] = 0;
len--;
} else {
break;
}
}
}
gboolean read_execp(void *obj)
{
Execp *execp = (Execp *)obj;
if (execp->backend->child_pipe_stdout < 0)
return FALSE;
gboolean stdout_eof, stderr_eof;
read_from_pipe(execp->backend->child_pipe_stdout,
&execp->backend->buf_stdout,
&execp->backend->buf_stdout_length,
&execp->backend->buf_stdout_capacity,
&stdout_eof);
read_from_pipe(execp->backend->child_pipe_stderr,
&execp->backend->buf_stderr,
&execp->backend->buf_stderr_length,
&execp->backend->buf_stderr_capacity,
&stderr_eof);
gboolean command_finished = stdout_eof && stderr_eof;
if (command_finished) {
execp->backend->child = 0;
close(execp->backend->child_pipe);
execp->backend->child_pipe = -1;
close(execp->backend->child_pipe_stdout);
execp->backend->child_pipe_stdout = -1;
close(execp->backend->child_pipe_stderr);
execp->backend->child_pipe_stderr = -1;
if (execp->backend->interval)
execp->backend->timer =
add_timeout(execp->backend->interval * 1000, 0, execp_timer_callback, execp, &execp->backend->timer);
change_timer(&execp->backend->timer, true, execp->backend->interval * 1000, 0, execp_timer_callback, execp);
}
char *ansi_clear_screen = (char*)"\x1b[2J";
if (!execp->backend->continuous && command_finished) {
// Handle stdout
free_and_null(execp->backend->text);
free_and_null(execp->backend->icon_path);
if (!execp->backend->has_icon) {
execp->backend->text = strdup(execp->backend->buf_output);
execp->backend->text = strdup(execp->backend->buf_stdout);
} else {
char *text = strchr(execp->backend->buf_output, '\n');
char *text = strchr(execp->backend->buf_stdout, '\n');
if (text) {
*text = '\0';
text++;
@@ -713,41 +781,75 @@ gboolean read_execp(void *obj)
} else {
execp->backend->text = strdup("");
}
execp->backend->icon_path = strdup(execp->backend->buf_output);
execp->backend->icon_path = strdup(execp->backend->buf_stdout);
}
int len = strlen(execp->backend->text);
if (len > 0 && execp->backend->text[len - 1] == '\n')
execp->backend->text[len - 1] = '\0';
execp->backend->buf_length = 0;
execp->backend->buf_output[execp->backend->buf_length] = '\0';
execp->backend->buf_stdout_length = 0;
execp->backend->buf_stdout[execp->backend->buf_stdout_length] = '\0';
// Handle stderr
if (!execp->backend->has_user_tooltip) {
free_and_null(execp->backend->tooltip);
char *start = last_substring(execp->backend->buf_stderr, ansi_clear_screen);
if (start)
start += strlen(ansi_clear_screen);
else
start = execp->backend->buf_stderr;
if (*start) {
execp->backend->tooltip = strdup(start);
rstrip(execp->backend->tooltip);
if (strlen(execp->backend->tooltip) > MAX_TOOLTIP_LEN)
execp->backend->tooltip[MAX_TOOLTIP_LEN] = '\0';
}
}
execp->backend->buf_stderr_length = 0;
execp->backend->buf_stderr[execp->backend->buf_stderr_length] = '\0';
//
execp->backend->last_update_finish_time = time(NULL);
execp->backend->last_update_duration =
execp->backend->last_update_finish_time - execp->backend->last_update_start_time;
return TRUE;
} else if (execp->backend->continuous > 0) {
// Handle stderr
if (!execp->backend->has_user_tooltip) {
free_and_null(execp->backend->tooltip);
char *start = last_substring(execp->backend->buf_stderr, ansi_clear_screen);
if (start) {
start += strlen(ansi_clear_screen);
memmove(execp->backend->buf_stderr, start, strlen(start) + 1);
execp->backend->buf_stderr_length = (ssize_t)strlen(execp->backend->buf_stderr);
}
if (execp->backend->buf_stderr_length > MAX_TOOLTIP_LEN) {
execp->backend->buf_stderr_length = MAX_TOOLTIP_LEN;
execp->backend->buf_stderr[execp->backend->buf_stderr_length] = '\0';
}
execp->backend->tooltip = strdup(execp->backend->buf_stderr);
rstrip(execp->backend->tooltip);
} else {
execp->backend->buf_stderr_length = 0;
execp->backend->buf_stderr[execp->backend->buf_stderr_length] = '\0';
}
// Handle stdout
// Count lines in buffer
int num_lines = 0;
char *last = execp->backend->buf_output;
char *end = NULL;
for (char *c = execp->backend->buf_output; *c; c++) {
for (char *c = execp->backend->buf_stdout; *c; c++) {
if (*c == '\n') {
num_lines++;
if (num_lines == execp->backend->continuous)
end = c;
}
last = c;
}
if (*last && *last != '\n')
num_lines++;
if (num_lines >= execp->backend->continuous) {
if (end)
*end = '\0';
free_and_null(execp->backend->text);
free_and_null(execp->backend->icon_path);
if (!execp->backend->has_icon) {
execp->backend->text = strdup(execp->backend->buf_output);
execp->backend->text = strdup(execp->backend->buf_stdout);
} else {
char *text = strchr(execp->backend->buf_output, '\n');
char *text = strchr(execp->backend->buf_stdout, '\n');
if (text) {
*text = '\0';
text++;
@@ -755,23 +857,23 @@ gboolean read_execp(void *obj)
} else {
execp->backend->text = strdup("");
}
execp->backend->icon_path = strdup(execp->backend->buf_output);
execp->backend->icon_path = strdup(execp->backend->buf_stdout);
}
int len = strlen(execp->backend->text);
size_t len = strlen(execp->backend->text);
if (len > 0 && execp->backend->text[len - 1] == '\n')
execp->backend->text[len - 1] = '\0';
if (end) {
char *next = end + 1;
int copied = next - execp->backend->buf_output;
int remaining = execp->backend->buf_length - copied;
ssize_t copied = next - execp->backend->buf_stdout;
ssize_t remaining = execp->backend->buf_stdout_length - copied;
if (remaining > 0) {
memmove(execp->backend->buf_output, next, remaining);
execp->backend->buf_length = remaining;
execp->backend->buf_output[execp->backend->buf_length] = '\0';
memmove(execp->backend->buf_stdout, next, (size_t)remaining);
execp->backend->buf_stdout_length = remaining;
execp->backend->buf_stdout[execp->backend->buf_stdout_length] = '\0';
} else {
execp->backend->buf_length = 0;
execp->backend->buf_output[execp->backend->buf_length] = '\0';
execp->backend->buf_stdout_length = 0;
execp->backend->buf_stdout[execp->backend->buf_stdout_length] = '\0';
}
}
@@ -784,29 +886,29 @@ gboolean read_execp(void *obj)
return FALSE;
}
const char *time_to_string(int seconds, char *buffer)
const char *time_to_string(int seconds, char *buffer, size_t buffer_size)
{
if (seconds < 60) {
sprintf(buffer, "%ds", seconds);
snprintf(buffer, buffer_size, "%ds", seconds);
} else if (seconds < 60 * 60) {
int m = seconds / 60;
seconds = seconds % 60;
int s = seconds;
sprintf(buffer, "%d:%ds", m, s);
snprintf(buffer, buffer_size, "%d:%ds", m, s);
} else {
int h = seconds / (60 * 60);
seconds = seconds % (60 * 60);
int m = seconds / 60;
seconds = seconds % 60;
int s = seconds;
sprintf(buffer, "%d:%d:%ds", h, m, s);
snprintf(buffer, buffer_size, "%d:%d:%ds", h, m, s);
}
return buffer;
}
char *execp_get_tooltip(void *obj)
{
Execp *execp = obj;
Execp *execp = (Execp *)obj;
if (execp->backend->tooltip) {
if (strlen(execp->backend->tooltip) > 0)
@@ -820,41 +922,86 @@ char *execp_get_tooltip(void *obj)
char tmp_buf1[256];
char tmp_buf2[256];
char tmp_buf3[256];
if (execp->backend->child_pipe < 0) {
if (execp->backend->child_pipe_stdout < 0) {
// Not executing command
if (execp->backend->last_update_finish_time) {
// We updated at least once
if (execp->backend->interval > 0) {
sprintf(execp->backend->tooltip_text,
"Last update finished %s ago (took %s). Next update starting in %s.",
time_to_string((int)(now - execp->backend->last_update_finish_time), tmp_buf1),
time_to_string((int)execp->backend->last_update_duration, tmp_buf2),
time_to_string((int)(execp->backend->interval - (now - execp->backend->last_update_finish_time)),
tmp_buf3));
snprintf(execp->backend->tooltip_text,
sizeof(execp->backend->tooltip_text),
"Last update finished %s ago (took %s). Next update starting in %s.",
time_to_string((int)(now - execp->backend->last_update_finish_time), tmp_buf1, sizeof(tmp_buf1)),
time_to_string((int)execp->backend->last_update_duration, tmp_buf2, sizeof(tmp_buf2)),
time_to_string((int)(execp->backend->interval - (now - execp->backend->last_update_finish_time)),
tmp_buf3, sizeof(tmp_buf3)));
} else {
sprintf(execp->backend->tooltip_text,
"Last update finished %s ago (took %s).",
time_to_string((int)(now - execp->backend->last_update_finish_time), tmp_buf1),
time_to_string((int)execp->backend->last_update_duration, tmp_buf2));
snprintf(execp->backend->tooltip_text,
sizeof(execp->backend->tooltip_text),
"Last update finished %s ago (took %s).",
time_to_string((int)(now - execp->backend->last_update_finish_time), tmp_buf1, sizeof(tmp_buf1)),
time_to_string((int)execp->backend->last_update_duration, tmp_buf2, sizeof(tmp_buf2)));
}
} else {
// we never requested an update
sprintf(execp->backend->tooltip_text, "Never updated. No update scheduled.");
snprintf(execp->backend->tooltip_text, sizeof(execp->backend->tooltip_text), "Never updated. No update scheduled.");
}
} else {
// Currently executing command
if (execp->backend->last_update_finish_time) {
// we finished updating at least once
sprintf(execp->backend->tooltip_text,
"Last update finished %s ago. Update in progress (started %s ago).",
time_to_string((int)(now - execp->backend->last_update_finish_time), tmp_buf1),
time_to_string((int)(now - execp->backend->last_update_start_time), tmp_buf3));
snprintf(execp->backend->tooltip_text,
sizeof(execp->backend->tooltip_text),
"Last update finished %s ago. Update in progress (started %s ago).",
time_to_string((int)(now - execp->backend->last_update_finish_time), tmp_buf1, sizeof(tmp_buf1)),
time_to_string((int)(now - execp->backend->last_update_start_time), tmp_buf3, sizeof(tmp_buf3)));
} else {
// we never finished an update
sprintf(execp->backend->tooltip_text,
"First update in progress (started %s seconds ago).",
time_to_string((int)(now - execp->backend->last_update_start_time), tmp_buf1));
snprintf(execp->backend->tooltip_text,
sizeof(execp->backend->tooltip_text),
"First update in progress (started %s seconds ago).",
time_to_string((int)(now - execp->backend->last_update_start_time), tmp_buf1, sizeof(tmp_buf1)));
}
}
return strdup(execp->backend->tooltip_text);
}
void execp_update_post_read(Execp *execp)
{
int icon_h, icon_w;
if (reload_icon(execp)) {
if (execp->backend->icon) {
imlib_context_set_image(execp->backend->icon);
icon_w = imlib_image_get_width();
icon_h = imlib_image_get_height();
} else {
icon_w = icon_h = 0;
}
} else {
icon_w = icon_h = 0;
}
if ((icon_h == 0 || icon_w == 0) && execp->backend->text[0] == 0) {
// Easy to test with bash -c 'R=$(( RANDOM % 2 )); [ $R -eq 0 ] && echo HELLO $R'
if (execp->area.on_screen)
hide(&execp->area);
} else {
if (!execp->area.on_screen)
show(&execp->area);
execp->area.resize_needed = TRUE;
schedule_panel_redraw();
}
}
void handle_execp_events()
{
for (GList *l = panel_config.execp_list; l; l = l->next) {
Execp *execp = (Execp *)l->data;
if (read_execp(execp)) {
GList *l_instance;
for (l_instance = execp->backend->instances; l_instance; l_instance = l_instance->next) {
Execp *instance = (Execp *)l_instance->data;
execp_update_post_read(instance);
}
}
}
}

View File

@@ -8,6 +8,8 @@
#include "common.h"
#include "timer.h"
extern bool debug_executors;
// Architecture:
// Panel panel_config contains an array of Execp, each storing all config options and all the state variables.
// Only these run commands.
@@ -27,6 +29,7 @@ typedef struct ExecpBackend {
gboolean cache_icon;
int icon_w;
int icon_h;
gboolean has_user_tooltip;
char *tooltip;
gboolean centered;
gboolean has_font;
@@ -45,21 +48,25 @@ typedef struct ExecpBackend {
Background *bg;
// Backend state:
timeout *timer;
int child_pipe;
Timer timer;
int child_pipe_stdout;
int child_pipe_stderr;
pid_t child;
// Command output buffer
char *buf_output;
int buf_length;
int buf_capacity;
char *buf_stdout;
ssize_t buf_stdout_length;
ssize_t buf_stdout_capacity;
char *buf_stderr;
ssize_t buf_stderr_length;
ssize_t buf_stderr_capacity;
// Text extracted from the output buffer
char *text;
// Icon path extracted from the output buffer
char *icon_path;
Imlib_Image icon;
char tooltip_text[512];
gchar tooltip_text[512];
// The time the last command was started
time_t last_update_start_time;
@@ -138,6 +145,11 @@ void execp_cmd_completed(Execp *obj, pid_t pid);
// Returns 1 if the output has been updated and a redraw is needed.
gboolean read_execp(void *obj);
// Called for Execp front elements when the command output has changed.
void execp_update_post_read(Execp *execp);
void execp_default_font_changed();
void handle_execp_events();
#endif // EXECPLUGIN_H

View File

@@ -65,14 +65,14 @@ void cleanup_freespace(Panel *panel)
panel->freespace_list = NULL;
}
int freespace_get_max_size(Panel *p)
int freespace_get_max_size(Panel *panel)
{
if (panel_shrink)
return 0;
// Get space used by every element except the freespace
int size = 0;
int spacers = 0;
for (GList *walk = p->area.children; walk; walk = g_list_next(walk)) {
for (GList *walk = panel->area.children; walk; walk = g_list_next(walk)) {
Area *a = (Area *)walk->data;
if (!a->on_screen)
@@ -83,15 +83,15 @@ int freespace_get_max_size(Panel *p)
}
if (panel_horizontal)
size += a->width + p->area.paddingx;
size += a->width + panel->area.paddingx * panel->scale;
else
size += a->height + p->area.paddingy;
size += a->height + panel->area.paddingy * panel->scale;
}
if (panel_horizontal)
size = p->area.width - size - left_right_border_width(&p->area) - p->area.paddingxlr;
size = panel->area.width - size - left_right_border_width(&panel->area) - panel->area.paddingxlr * panel->scale;
else
size = p->area.height - size - top_bottom_border_width(&p->area) - p->area.paddingxlr;
size = panel->area.height - size - top_bottom_border_width(&panel->area) - panel->area.paddingxlr * panel->scale;
return size / spacers;
}

311
src/init.c Normal file
View File

@@ -0,0 +1,311 @@
#include "init.h"
#include <locale.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <X11/Xlib.h>
#include <X11/Xutil.h>
#include <X11/Xatom.h>
#include <X11/extensions/XShm.h>
#include "config.h"
#include "default_icon.h"
#include "drag_and_drop.h"
#include "fps_distribution.h"
#include "panel.h"
#include "server.h"
#include "signals.h"
#include "test.h"
#include "tooltip.h"
#include "tracing.h"
#include "uevent.h"
#include "version.h"
void print_usage()
{
fprintf(stdout,
"Usage: tint2 [OPTION...]\n"
"\n"
"Options:\n"
" -c path_to_config_file Loads the configuration file from a\n"
" custom location.\n"
" -v, --version Prints version information and exits.\n"
" -h, --help Display this help and exits.\n"
"\n"
"For more information, run `man tint2` or visit the project page\n"
"<https://gitlab.com/o9000/tint2>.\n");
}
void handle_cli_arguments(int argc, char **argv)
{
// Read command line arguments
for (int i = 1; i < argc; ++i) {
gboolean error = FALSE;
if (strcmp(argv[i], "-h") == 0 || strcmp(argv[i], "--help") == 0) {
print_usage();
exit(0);
} else if (strcmp(argv[i], "-v") == 0 || strcmp(argv[i], "--version") == 0) {
fprintf(stdout, "tint2 version %s\n", VERSION_STRING);
exit(0);
} else if (strcmp(argv[i], "--test") == 0) {
run_all_tests(false);
exit(0);
} else if (strcmp(argv[i], "--test-verbose") == 0) {
run_all_tests(true);
exit(0);
} else if (strcmp(argv[i], "--dump-image-data") == 0) {
dump_image_data(argv[i+1], argv[i+2]);
exit(0);
} else if (strcmp(argv[i], "-c") == 0) {
if (i + 1 < argc) {
i++;
config_path = strdup(argv[i]);
} else {
error = TRUE;
}
} else if (strcmp(argv[i], "-s") == 0) {
if (i + 1 < argc) {
i++;
snapshot_path = strdup(argv[i]);
} else {
error = TRUE;
}
} else if (i + 1 == argc) {
config_path = strdup(argv[i]);
}
#ifdef ENABLE_BATTERY
else if (strcmp(argv[i], "--battery-sys-prefix") == 0) {
if (i + 1 < argc) {
i++;
battery_sys_prefix = strdup(argv[i]);
} else {
error = TRUE;
}
}
#endif
else {
error = TRUE;
}
if (error) {
print_usage();
exit(EXIT_FAILURE);
}
}
}
void handle_env_vars()
{
debug_geometry = getenv("DEBUG_GEOMETRY") != NULL;
debug_gradients = getenv("DEBUG_GRADIENTS") != NULL;
debug_icons = getenv("DEBUG_ICONS") != NULL;
debug_fps = getenv("DEBUG_FPS") != NULL;
debug_frames = getenv("DEBUG_FRAMES") != NULL;
debug_dnd = getenv("DEBUG_DND") != NULL;
debug_thumbnails = getenv("DEBUG_THUMBNAILS") != NULL;
debug_timers = getenv("DEBUG_TIMERS") != NULL;
debug_executors = getenv("DEBUG_EXECUTORS") != NULL;
if (debug_fps) {
init_fps_distribution();
char *s = getenv("TRACING_FPS_THRESHOLD");
if (!s || sscanf(s, "%lf", &tracing_fps_threshold) != 1) {
tracing_fps_threshold = 60;
}
}
}
static Timer detect_compositor_timer = DEFAULT_TIMER;
static int detect_compositor_timer_counter = 0;
void detect_compositor(void *arg)
{
if (server.composite_manager) {
stop_timer(&detect_compositor_timer);
return;
}
detect_compositor_timer_counter--;
if (detect_compositor_timer_counter < 0) {
stop_timer(&detect_compositor_timer);
return;
}
// No compositor, check for one
if (XGetSelectionOwner(server.display, server.atom._NET_WM_CM_S0) != None) {
stop_timer(&detect_compositor_timer);
// Restart tint2
fprintf(stderr, "tint2: Detected compositor, restarting tint2...\n");
kill(getpid(), SIGUSR1);
}
}
void start_detect_compositor()
{
// Already have a compositor, nothing to do
if (server.composite_manager)
return;
// Check every 0.5 seconds for up to 30 seconds
detect_compositor_timer_counter = 60;
INIT_TIMER(detect_compositor_timer);
change_timer(&detect_compositor_timer, true, 500, 500, detect_compositor, 0);
}
void create_default_elements()
{
default_timers();
default_systray();
memset(&server, 0, sizeof(server));
#ifdef ENABLE_BATTERY
default_battery();
#endif
default_clock();
default_launcher();
default_taskbar();
default_tooltip();
default_execp();
default_button();
default_panel();
}
void load_default_task_icon()
{
const gchar *const *data_dirs = g_get_system_data_dirs();
for (int i = 0; data_dirs[i] != NULL; i++) {
gchar *path = g_build_filename(data_dirs[i], "tint2", "default_icon.png", NULL);
if (g_file_test(path, G_FILE_TEST_EXISTS))
default_icon = load_image(path, TRUE);
g_free(path);
}
if (!default_icon) {
default_icon = imlib_create_image_using_data(default_icon_width,
default_icon_height,
default_icon_data);
}
}
void init_post_config()
{
server_init_visual();
server_init_xdamage();
imlib_context_set_display(server.display);
imlib_context_set_visual(server.visual);
imlib_context_set_colormap(server.colormap);
init_signals_postconfig();
load_default_task_icon();
XSync(server.display, False);
}
void init_X11_pre_config()
{
server.display = XOpenDisplay(NULL);
if (!server.display) {
fprintf(stderr, "tint2: could not open display!\n");
exit(EXIT_FAILURE);
}
server.x11_fd = ConnectionNumber(server.display);
XSetErrorHandler((XErrorHandler)server_catch_error);
XSetIOErrorHandler((XIOErrorHandler)x11_io_error);
server_init_atoms();
server.screen = DefaultScreen(server.display);
server.root_win = RootWindow(server.display, server.screen);
server.desktop = get_current_desktop();
server.has_shm = XShmQueryExtension(server.display);
// Needed since the config file uses '.' as decimal separator
setlocale(LC_ALL, "");
setlocale(LC_NUMERIC, "POSIX");
/* Catch events */
XSelectInput(server.display, server.root_win, PropertyChangeMask | StructureNotifyMask);
// get monitor and desktop config
get_monitors();
get_desktops();
server.disable_transparency = 0;
xsettings_client = xsettings_client_new(server.display, server.screen, xsettings_notify_cb, NULL, NULL);
}
void init(int argc, char **argv)
{
setlinebuf(stdout);
setlinebuf(stderr);
default_config();
handle_env_vars();
handle_cli_arguments(argc, argv);
create_default_elements();
init_signals();
init_X11_pre_config();
if (!config_read()) {
fprintf(stderr, "tint2: Could not read config file.\n");
print_usage();
cleanup();
return;
}
init_post_config();
start_detect_compositor();
init_panel();
}
void cleanup()
{
#ifdef HAVE_SN
if (startup_notifications) {
sn_display_unref(server.sn_display);
server.sn_display = NULL;
}
#endif // HAVE_SN
cleanup_button();
cleanup_execp();
cleanup_systray();
cleanup_tooltip();
cleanup_clock();
cleanup_launcher();
#ifdef ENABLE_BATTERY
cleanup_battery();
#endif
cleanup_separator();
cleanup_taskbar();
cleanup_panel();
cleanup_config();
if (default_icon) {
imlib_context_set_image(default_icon);
imlib_free_image();
default_icon = NULL;
}
imlib_context_disconnect_display();
xsettings_client_destroy(xsettings_client);
xsettings_client = NULL;
cleanup_server();
cleanup_timers();
if (server.display)
XCloseDisplay(server.display);
server.display = NULL;
if (sigchild_pipe_valid) {
sigchild_pipe_valid = FALSE;
close(sigchild_pipe[1]);
close(sigchild_pipe[0]);
}
uevent_cleanup();
cleanup_fps_distribution();
#ifdef HAVE_TRACING
cleanup_tracing();
#endif
}

7
src/init.h Normal file
View File

@@ -0,0 +1,7 @@
#ifndef INIT_H
#define INIT_H
void init(int argc, char **argv);
void cleanup();
#endif

View File

@@ -60,9 +60,9 @@ void expand_exec(DesktopEntry *entry, const char *path)
// %c -> Name
// %k -> path
if (entry->exec) {
char *exec2 = calloc(strlen(entry->exec) + (entry->name ? strlen(entry->name) : 1) +
(entry->icon ? strlen(entry->icon) : 1) + 100,
1);
size_t buf_size = strlen(entry->exec) + (entry->name ? strlen(entry->name) : 1) +
(entry->icon ? strlen(entry->icon) : 1) + 100;
char *exec2 = calloc(buf_size, 1);
char *p, *q;
// p will never point to an escaped char
for (p = entry->exec, q = exec2; *p; p++, q++) {
@@ -82,19 +82,30 @@ void expand_exec(DesktopEntry *entry, const char *path)
if (!*p)
break;
if (*p == 'i' && entry->icon != NULL) {
sprintf(q, "--icon '%s'", entry->icon);
snprintf(q, buf_size, "--icon '%s'", entry->icon);
char *old = q;
q += strlen("--icon ''");
q += strlen(entry->icon);
buf_size -= (size_t)(q - old);
q--; // To balance the q++ in the for
} else if (*p == 'c' && entry->name != NULL) {
sprintf(q, "'%s'", entry->name);
snprintf(q, buf_size, "'%s'", entry->name);
char *old = q;
q += strlen("''");
q += strlen(entry->name);
buf_size -= (size_t)(q - old);
q--; // To balance the q++ in the for
} else if (*p == 'c') {
sprintf(q, "'%s'", path);
snprintf(q, buf_size, "'%s'", path);
char *old = q;
q += strlen("''");
q += strlen(path);
buf_size -= (size_t)(q - old);
q--; // To balance the q++ in the for
} else if (*p == 'f' || *p == 'F') {
snprintf(q, buf_size, "%c%c", '%', *p);
q += 2;
buf_size -= 2;
q--; // To balance the q++ in the for
} else {
// We don't care about other expansions
@@ -113,10 +124,12 @@ gboolean read_desktop_file_full_path(const char *path, DesktopEntry *entry)
{
entry->name = entry->generic_name = entry->icon = entry->exec = entry->cwd = NULL;
entry->hidden_from_menus = FALSE;
entry->start_in_terminal = FALSE;
entry->startup_notification = TRUE;
FILE *fp = fopen(path, "rt");
if (fp == NULL) {
fprintf(stderr, "Could not open file %s\n", path);
fprintf(stderr, "tint2: Could not open file %s\n", path);
return FALSE;
}
@@ -126,14 +139,14 @@ gboolean read_desktop_file_full_path(const char *path, DesktopEntry *entry)
int lang_index_default = 1;
#define LANG_DBG 0
if (LANG_DBG)
printf("Languages:");
fprintf(stderr, "tint2: Languages:");
for (int i = 0; languages[i]; i++) {
lang_index_default = i + 1;
if (LANG_DBG)
printf(" %s", languages[i]);
fprintf(stderr, "tint2: %s", languages[i]);
}
if (LANG_DBG)
printf("\n");
fprintf(stderr, "tint2: \n");
// we currently do not know about any Name key at all, so use an invalid index
int lang_index_name = lang_index_default + 1;
int lang_index_generic_name = lang_index_default + 1;
@@ -192,6 +205,10 @@ gboolean read_desktop_file_full_path(const char *path, DesktopEntry *entry)
entry->icon = strdup(value);
} else if (strcmp(key, "NoDisplay") == 0) {
entry->hidden_from_menus = strcasecmp(value, "true") == 0;
} else if (strcmp(key, "Terminal") == 0) {
entry->start_in_terminal = strcasecmp(value, "true") == 0;
} else if (strcmp(key, "StartupNotify") == 0) {
entry->startup_notification = strcasecmp(value, "true") == 0;
}
}
}
@@ -278,11 +295,11 @@ void free_desktop_entry(DesktopEntry *entry)
void test_read_desktop_file()
{
fprintf(stdout, "\033[1;33m");
fprintf(stderr, YELLOW);
DesktopEntry entry;
read_desktop_file("/usr/share/applications/firefox.desktop", &entry);
printf("Name:%s GenericName:%s Icon:%s Exec:%s\n", entry.name, entry.generic_name, entry.icon, entry.exec);
fprintf(stdout, "\033[0m");
fprintf(stderr, "tint2: Name:%s GenericName:%s Icon:%s Exec:%s\n", entry.name, entry.generic_name, entry.icon, entry.exec);
fprintf(stderr, RESET);
}
GSList *apps_locations = NULL;

View File

@@ -17,6 +17,8 @@ typedef struct DesktopEntry {
char *path;
char *cwd;
gboolean hidden_from_menus;
gboolean start_in_terminal;
gboolean startup_notification;
} DesktopEntry;
// Parses a line of the form "key = value". Modifies the line.

View File

@@ -29,6 +29,8 @@
#include "common.h"
#include "cache.h"
gboolean debug_icons = FALSE;
#define ICON_DIR_TYPE_SCALABLE 0
#define ICON_DIR_TYPE_FIXED 1
#define ICON_DIR_TYPE_THRESHOLD 2
@@ -46,7 +48,7 @@ int parse_theme_line(char *line, char **key, char **value)
return parse_dektop_line(line, key, value);
}
GSList *icon_locations = NULL;
static GSList *icon_locations = NULL;
// Do not free the result.
const GSList *get_icon_locations()
{
@@ -72,6 +74,21 @@ const GSList *get_icon_locations()
return icon_locations;
}
static GSList *icon_extensions = NULL;
const GSList *get_icon_extensions()
{
if (icon_extensions)
return icon_extensions;
icon_extensions = g_slist_append(icon_extensions, ".png");
icon_extensions = g_slist_append(icon_extensions, ".xpm");
#ifdef HAVE_RSVG
icon_extensions = g_slist_append(icon_extensions, ".svg");
#endif
icon_extensions = g_slist_append(icon_extensions, "");
return icon_extensions;
}
IconTheme *make_theme(const char *name)
{
IconTheme *theme = calloc(1, sizeof(IconTheme));
@@ -90,7 +107,7 @@ IconTheme *load_theme_from_index(const char *file_name, const char *name)
size_t line_size;
if ((f = fopen(file_name, "rt")) == NULL) {
fprintf(stderr, "Could not open theme '%s'\n", file_name);
fprintf(stderr, "tint2: Could not open theme '%s'\n", file_name);
return NULL;
}
@@ -325,22 +342,22 @@ void free_themes(IconThemeWrapper *wrapper)
void test_launcher_read_theme_file()
{
fprintf(stdout, "\033[1;33m");
fprintf(stdout, YELLOW);
IconTheme *theme = load_theme("oxygen");
if (!theme) {
printf("Could not load theme\n");
fprintf(stderr, "tint2: Could not load theme\n");
return;
}
printf("Loaded theme: %s\n", theme->name);
fprintf(stderr, "tint2: Loaded theme: %s\n", theme->name);
GSList *item = theme->list_inherits;
while (item != NULL) {
printf("Inherits:%s\n", (char *)item->data);
fprintf(stderr, "tint2: Inherits:%s\n", (char *)item->data);
item = g_slist_next(item);
}
item = theme->list_directories;
while (item != NULL) {
IconThemeDir *dir = item->data;
printf("Dir:%s Size=%d MinSize=%d MaxSize=%d Threshold=%d Type=%s\n",
fprintf(stderr, "tint2: Dir:%s Size=%d MinSize=%d MaxSize=%d Threshold=%d Type=%s\n",
dir->name,
dir->size,
dir->min_size,
@@ -352,7 +369,7 @@ void test_launcher_read_theme_file()
: "?????");
item = g_slist_next(item);
}
fprintf(stdout, "\033[0m");
fprintf(stdout, RESET);
}
gboolean str_list_contains(const GSList *list, const char *value)
@@ -379,7 +396,7 @@ void load_themes_helper(const char *name, GSList **themes, GSList **queued)
char *queued_name = queue->data;
queue = g_slist_remove(queue, queued_name);
fprintf(stderr, " '%s',", queued_name);
fprintf(stderr, "tint2: '%s',", queued_name);
IconTheme *theme = load_theme(queued_name);
if (theme != NULL) {
*themes = g_slist_append(*themes, theme);
@@ -399,7 +416,7 @@ void load_themes_helper(const char *name, GSList **themes, GSList **queued)
free(queued_name);
}
fprintf(stderr, "\n");
fprintf(stderr, "tint2: \n");
// Free the queue
GSList *l;
@@ -413,7 +430,7 @@ void load_default_theme(IconThemeWrapper *wrapper)
if (wrapper->_themes_loaded)
return;
fprintf(stderr, GREEN "Loading icon theme %s:" RESET "\n", wrapper->icon_theme_name);
fprintf(stderr, GREEN "tint2: Loading icon theme %s:" RESET "\n", wrapper->icon_theme_name);
load_themes_helper(wrapper->icon_theme_name, &wrapper->themes, &wrapper->_queued);
load_themes_helper("hicolor", &wrapper->themes, &wrapper->_queued);
@@ -426,7 +443,7 @@ void load_fallbacks(IconThemeWrapper *wrapper)
if (wrapper->_fallback_loaded)
return;
fprintf(stderr, RED "Loading additional icon themes (this means your icon theme is incomplete)..." RESET "\n");
fprintf(stderr, RED "tint2: Loading additional icon themes (this means your icon theme is incomplete)..." RESET "\n");
// Load wrapper->themes_fallback
const GSList *location;
@@ -459,7 +476,7 @@ void load_icon_cache(IconThemeWrapper *wrapper)
if (wrapper->_cache.loaded)
return;
fprintf(stderr, GREEN "Loading icon theme cache..." RESET "\n");
fprintf(stderr, GREEN "tint2: Loading icon theme cache..." RESET "\n");
gchar *cache_path = get_icon_cache_path();
load_cache(&wrapper->_cache, cache_path);
@@ -471,7 +488,7 @@ void save_icon_cache(IconThemeWrapper *wrapper)
if (!wrapper || !wrapper->_cache.dirty)
return;
fprintf(stderr, GREEN "Saving icon theme cache..." RESET "\n");
fprintf(stderr, GREEN "tint2: Saving icon theme cache..." RESET "\n");
gchar *cache_path = get_icon_cache_path();
save_cache(&wrapper->_cache, cache_path);
g_free(cache_path);
@@ -482,7 +499,7 @@ IconThemeWrapper *load_themes(const char *icon_theme_name)
IconThemeWrapper *wrapper = calloc(1, sizeof(IconThemeWrapper));
if (!icon_theme_name) {
fprintf(stderr, "Missing icon_theme_name theme, default to 'hicolor'.\n");
fprintf(stderr, "tint2: Missing icon_theme_name theme, default to 'hicolor'.\n");
icon_theme_name = "hicolor";
}
@@ -533,42 +550,42 @@ gint compare_theme_directories(gconstpointer a, gconstpointer b, gpointer size_q
return abs(da->size - size) - abs(db->size - size);
}
#define DEBUG_ICON_SEARCH 0
Bool is_full_path(const char *s)
{
if (!s)
return FALSE;
return s[0] == '/';
}
Bool file_exists(const char *path)
{
return g_file_test(path, G_FILE_TEST_EXISTS);
}
char *icon_path_from_full_path(const char *s)
{
if (is_full_path(s) && file_exists(s))
return strdup(s);
return NULL;
}
char *get_icon_path_helper(GSList *themes, const char *icon_name, int size)
{
if (icon_name == NULL)
if (!icon_name)
return NULL;
// If the icon_name is already a path and the file exists, return it
if (strstr(icon_name, "/") == icon_name) {
if (g_file_test(icon_name, G_FILE_TEST_EXISTS))
return strdup(icon_name);
else
return NULL;
}
char *result = icon_path_from_full_path(icon_name);
if (result)
return result;
const GSList *basenames = get_icon_locations();
GSList *extensions = NULL;
extensions = g_slist_append(extensions, ".png");
extensions = g_slist_append(extensions, ".xpm");
#ifdef HAVE_RSVG
extensions = g_slist_append(extensions, ".svg");
#endif
// if the icon name already contains one of the extensions (e.g. vlc.png instead of vlc) add a special entry
for (GSList *ext = extensions; ext; ext = g_slist_next(ext)) {
char *extension = (char *)ext->data;
if (strlen(icon_name) > strlen(extension) &&
strcmp(extension, icon_name + strlen(icon_name) - strlen(extension)) == 0) {
extensions = g_slist_append(extensions, "");
break;
}
}
const GSList *extensions = get_icon_extensions();
GSList *theme;
// Best size match
// Contrary to the freedesktop spec, we are not choosing the closest icon in size, but the next larger icon
// otherwise the quality is usually crap (for size 22, if you can choose 16 or 32, you're better with 32)
// otherwise the quality is worse when scaling up (for size 22, if you can choose 16 or 32, you're better with 32)
// We do fallback to the closest size if we cannot find a larger or equal icon
// These 3 variables are used for keeping the closest size match
@@ -581,10 +598,12 @@ char *get_icon_path_helper(GSList *themes, const char *icon_name, int size)
char *next_larger = NULL;
GSList *next_larger_theme = NULL;
int file_name_size = 4096;
size_t file_name_size = 4096;
char *file_name = calloc(file_name_size, 1);
for (theme = themes; theme; theme = g_slist_next(theme)) {
if (debug_icons)
fprintf(stderr, "tint2: Searching theme: %s\n", ((IconTheme *)theme->data)->name);
((IconTheme *)theme->data)->list_directories =
g_slist_sort_with_data(((IconTheme *)theme->data)->list_directories,
compare_theme_directories,
@@ -600,9 +619,11 @@ char *get_icon_path_helper(GSList *themes, const char *icon_name, int size)
(!next_larger_theme ? 1 : theme == next_larger_theme));
if (!possible)
continue;
if (debug_icons)
fprintf(stderr, "tint2: Searching directory: %s\n", ((IconThemeDir *)dir->data)->name);
const GSList *base;
for (base = basenames; base; base = g_slist_next(base)) {
for (GSList *ext = extensions; ext; ext = g_slist_next(ext)) {
for (const GSList *ext = extensions; ext; ext = g_slist_next(ext)) {
char *base_name = (char *)base->data;
char *theme_name = ((IconTheme *)theme->data)->name;
char *dir_name = ((IconThemeDir *)dir->data)->name;
@@ -616,12 +637,12 @@ char *get_icon_path_helper(GSList *themes, const char *icon_name, int size)
}
file_name[0] = 0;
// filename = directory/$(themename)/subdirectory/iconname.extension
sprintf(file_name, "%s/%s/%s/%s%s", base_name, theme_name, dir_name, icon_name, extension);
if (DEBUG_ICON_SEARCH)
printf("checking %s\n", file_name);
snprintf(file_name, (size_t)file_name_size, "%s/%s/%s/%s%s", base_name, theme_name, dir_name, icon_name, extension);
if (debug_icons)
fprintf(stderr, "tint2: Checking %s\n", file_name);
if (g_file_test(file_name, G_FILE_TEST_EXISTS)) {
if (DEBUG_ICON_SEARCH)
printf("found: %s\n", file_name);
if (debug_icons)
fprintf(stderr, "tint2: Found potential match: %s\n", file_name);
// Closest match
if (directory_size_distance((IconThemeDir *)dir->data, size) < minimal_size &&
(!best_file_theme ? 1 : theme == best_file_theme)) {
@@ -632,8 +653,8 @@ char *get_icon_path_helper(GSList *themes, const char *icon_name, int size)
best_file_name = strdup(file_name);
minimal_size = directory_size_distance((IconThemeDir *)dir->data, size);
best_file_theme = theme;
if (DEBUG_ICON_SEARCH)
printf("best_file_name = %s; minimal_size = %d\n", best_file_name, minimal_size);
if (debug_icons)
fprintf(stderr, "tint2: best_file_name = %s; minimal_size = %d\n", best_file_name, minimal_size);
}
// Next larger match
if (((IconThemeDir *)dir->data)->size >= size &&
@@ -646,8 +667,8 @@ char *get_icon_path_helper(GSList *themes, const char *icon_name, int size)
next_larger = strdup(file_name);
next_larger_size = ((IconThemeDir *)dir->data)->size;
next_larger_theme = theme;
if (DEBUG_ICON_SEARCH)
printf("next_larger = %s; next_larger_size = %d\n", next_larger, next_larger_size);
if (debug_icons)
fprintf(stderr, "tint2: next_larger = %s; next_larger_size = %d\n", next_larger, next_larger_size);
}
}
}
@@ -657,28 +678,30 @@ char *get_icon_path_helper(GSList *themes, const char *icon_name, int size)
free(file_name);
file_name = NULL;
if (next_larger) {
g_slist_free(extensions);
free(best_file_name);
return next_larger;
}
if (best_file_name) {
g_slist_free(extensions);
return best_file_name;
}
// Look in unthemed icons
{
if (debug_icons)
fprintf(stderr, "tint2: Searching unthemed icons\n");
for (const GSList *base = basenames; base; base = g_slist_next(base)) {
for (GSList *ext = extensions; ext; ext = g_slist_next(ext)) {
for (const GSList *ext = extensions; ext; ext = g_slist_next(ext)) {
char *base_name = (char *)base->data;
char *extension = (char *)ext->data;
file_name = calloc(strlen(base_name) + strlen(icon_name) + strlen(extension) + 100, 1);
size_t file_name_size2 = strlen(base_name) + strlen(icon_name) + strlen(extension) + 100;
file_name = calloc(file_name_size2, 1);
// filename = directory/iconname.extension
sprintf(file_name, "%s/%s%s", base_name, icon_name, extension);
if (DEBUG_ICON_SEARCH)
printf("checking %s\n", file_name);
snprintf(file_name, file_name_size2, "%s/%s%s", base_name, icon_name, extension);
if (debug_icons)
fprintf(stderr, "tint2: Checking %s\n", file_name);
if (g_file_test(file_name, G_FILE_TEST_EXISTS)) {
g_slist_free(extensions);
if (debug_icons)
fprintf(stderr, "tint2: Found %s\n", file_name);
return file_name;
} else {
free(file_name);
@@ -688,7 +711,6 @@ char *get_icon_path_helper(GSList *themes, const char *icon_name, int size)
}
}
g_slist_free(extensions);
return NULL;
}
@@ -715,7 +737,7 @@ char *get_icon_path_from_cache(IconThemeWrapper *wrapper, const char *icon_name,
if (!g_file_test(value, G_FILE_TEST_EXISTS))
return NULL;
// fprintf(stderr, "Icon path found in cache: theme = %s, icon = %s, size = %d, path = %s\n",
// fprintf(stderr, "tint2: Icon path found in cache: theme = %s, icon = %s, size = %d, path = %s\n",
// wrapper->icon_theme_name, icon_name, size, value);
return strdup(value);
@@ -742,28 +764,44 @@ void add_icon_path_to_cache(IconThemeWrapper *wrapper, const char *icon_name, in
char *get_icon_path(IconThemeWrapper *wrapper, const char *icon_name, int size, gboolean use_fallbacks)
{
if (!wrapper)
if (debug_icons)
fprintf(stderr,
"Searching for icon %s with size %d, fallbacks %sallowed\n",
icon_name,
size,
use_fallbacks ? "" : "not ");
if (!wrapper) {
if (debug_icons)
fprintf(stderr,
"Icon search aborted, themes not loaded\n");
return NULL;
}
if (!icon_name || strlen(icon_name) == 0)
goto notfound;
char *path = get_icon_path_from_cache(wrapper, icon_name, size);
if (path)
if (path) {
if (debug_icons)
fprintf(stderr,
"Icon found in cache: %s\n", path);
return path;
}
load_default_theme(wrapper);
icon_name = icon_name ? icon_name : DEFAULT_ICON;
path = get_icon_path_helper(wrapper->themes, icon_name, size);
if (path) {
if (debug_icons)
fprintf(stderr, "tint2: Icon found: %s\n", path);
add_icon_path_to_cache(wrapper, icon_name, size, path);
return path;
}
if (!use_fallbacks)
goto notfound;
fprintf(stderr, YELLOW "Icon not found in default theme: %s" RESET "\n", icon_name);
fprintf(stderr, YELLOW "tint2: Icon not found in default theme: %s" RESET "\n", icon_name);
load_fallbacks(wrapper);
path = get_icon_path_helper(wrapper->themes_fallback, icon_name, size);
@@ -773,7 +811,7 @@ char *get_icon_path(IconThemeWrapper *wrapper, const char *icon_name, int size,
}
notfound:
fprintf(stderr, RED "Could not find icon '%s', using default." RESET "\n", icon_name);
fprintf(stderr, RED "tint2: Could not find icon '%s', using default." RESET "\n", icon_name);
path = get_icon_path_helper(wrapper->themes, DEFAULT_ICON, size);
if (path)
return path;

View File

@@ -57,4 +57,6 @@ char *get_icon_path(IconThemeWrapper *wrapper, const char *icon_name, int size,
// Do not free the result, it is cached.
const GSList *get_icon_locations();
extern gboolean debug_icons;
#endif

View File

@@ -64,6 +64,8 @@ void launcher_reload_hidden_icons(Launcher *launcher);
void launcher_icon_on_change_layout(void *obj);
int launcher_compute_desired_size(void *obj);
void relayout_launcher();
void default_launcher()
{
launcher_enabled = 0;
@@ -95,6 +97,7 @@ void init_launcher_panel(void *p)
launcher->area._draw_foreground = NULL;
launcher->area.size_mode = LAYOUT_FIXED;
launcher->area._resize = resize_launcher;
launcher->area._on_change_layout = relayout_launcher;
launcher->area._compute_desired_size = launcher_compute_desired_size;
launcher->area.resize_needed = 1;
schedule_redraw(&launcher->area);
@@ -109,7 +112,7 @@ void init_launcher_panel(void *p)
return;
// This will be recomputed on resize, we just initialize to a non-zero value
launcher->icon_size = launcher_max_icon_size > 0 ? launcher_max_icon_size : 24;
launcher->icon_size = launcher_max_icon_size > 0 ? launcher_max_icon_size * panel->scale : 24;
launcher->area.on_screen = TRUE;
schedule_panel_redraw();
@@ -171,11 +174,12 @@ void cleanup_launcher_theme(Launcher *launcher)
int launcher_compute_icon_size(Launcher *launcher)
{
Panel *panel = launcher->area.panel;
int icon_size = panel_horizontal ? launcher->area.height : launcher->area.width;
icon_size = icon_size - MAX(left_right_border_width(&launcher->area), top_bottom_border_width(&launcher->area)) -
(2 * launcher->area.paddingy);
if (launcher_max_icon_size > 0 && icon_size > launcher_max_icon_size)
icon_size = launcher_max_icon_size;
(2 * launcher->area.paddingy * panel->scale);
if (launcher_max_icon_size)
icon_size = MIN(icon_size, launcher_max_icon_size * panel->scale);
return icon_size;
}
@@ -186,6 +190,7 @@ void launcher_compute_geometry(Launcher *launcher,
int *icons_per_row,
int *margin)
{
Panel *panel = (Panel*)launcher->area.panel;
int count = 0;
for (GSList *l = launcher->list_icons; l; l = l->next) {
LauncherIcon *launcherIcon = (LauncherIcon *)l->data;
@@ -201,25 +206,25 @@ void launcher_compute_geometry(Launcher *launcher,
if (!count) {
*size = 0;
} else {
int height = launcher->area.height - top_bottom_border_width(&launcher->area) - 2 * launcher->area.paddingy;
int height = launcher->area.height - top_bottom_border_width(&launcher->area) - 2 * launcher->area.paddingy * panel->scale;
// here icons_per_column always higher than 0
*icons_per_column = (height + launcher->area.paddingx) / (*icon_size + launcher->area.paddingx);
*margin = height - (*icons_per_column - 1) * (*icon_size + launcher->area.paddingx) - *icon_size;
*icons_per_column = (height + launcher->area.paddingx * panel->scale) / (*icon_size + launcher->area.paddingx * panel->scale);
*margin = height - (*icons_per_column - 1) * (*icon_size + launcher->area.paddingx * panel->scale) - *icon_size;
*icons_per_row = count / *icons_per_column + (count % *icons_per_column != 0);
*size = left_right_border_width(&launcher->area) + 2 * launcher->area.paddingxlr +
(*icon_size * *icons_per_row) + ((*icons_per_row - 1) * launcher->area.paddingx);
*size = left_right_border_width(&launcher->area) + 2 * launcher->area.paddingxlr * panel->scale +
(*icon_size * *icons_per_row) + ((*icons_per_row - 1) * launcher->area.paddingx * panel->scale);
}
} else {
if (!count) {
*size = 0;
} else {
int width = launcher->area.width - top_bottom_border_width(&launcher->area) - 2 * launcher->area.paddingy;
int width = launcher->area.width - top_bottom_border_width(&launcher->area) - 2 * launcher->area.paddingy * panel->scale;
// here icons_per_row always higher than 0
*icons_per_row = (width + launcher->area.paddingx) / (*icon_size + launcher->area.paddingx);
*margin = width - (*icons_per_row - 1) * (*icon_size + launcher->area.paddingx) - *icon_size;
*icons_per_row = (width + launcher->area.paddingx * panel->scale) / (*icon_size + launcher->area.paddingx * panel->scale);
*margin = width - (*icons_per_row - 1) * (*icon_size + launcher->area.paddingx * panel->scale) - *icon_size;
*icons_per_column = count / *icons_per_row + (count % *icons_per_row != 0);
*size = top_bottom_border_width(&launcher->area) + 2 * launcher->area.paddingxlr +
(*icon_size * *icons_per_column) + ((*icons_per_column - 1) * launcher->area.paddingx);
*size = top_bottom_border_width(&launcher->area) + 2 * launcher->area.paddingxlr * panel->scale +
(*icon_size * *icons_per_column) + ((*icons_per_column - 1) * launcher->area.paddingx * panel->scale);
}
}
}
@@ -236,6 +241,7 @@ int launcher_compute_desired_size(void *obj)
gboolean resize_launcher(void *obj)
{
Launcher *launcher = (Launcher *)obj;
Panel *panel = (Panel*)launcher->area.panel;
int size, icons_per_column, icons_per_row, margin;
launcher_compute_geometry(launcher, &size, &launcher->icon_size, &icons_per_column, &icons_per_row, &margin);
@@ -278,11 +284,11 @@ gboolean resize_launcher(void *obj)
int posx, posy;
int start;
if (panel_horizontal) {
posy = start = top_border_width(&launcher->area) + launcher->area.paddingy + margin / 2;
posx = left_border_width(&launcher->area) + launcher->area.paddingxlr;
posy = start = top_border_width(&launcher->area) + launcher->area.paddingy * panel->scale + margin / 2;
posx = left_border_width(&launcher->area) + launcher->area.paddingxlr * panel->scale;
} else {
posx = start = left_border_width(&launcher->area) + launcher->area.paddingy + margin / 2;
posy = top_border_width(&launcher->area) + launcher->area.paddingxlr;
posx = start = left_border_width(&launcher->area) + launcher->area.paddingy * panel->scale + margin / 2;
posy = top_border_width(&launcher->area) + launcher->area.paddingxlr * panel->scale;
}
int i = 0;
@@ -294,20 +300,20 @@ gboolean resize_launcher(void *obj)
launcherIcon->y = posy;
launcherIcon->x = posx;
launcher_icon_on_change_layout(launcherIcon);
// printf("launcher %d : %d,%d\n", i, posx, posy);
// fprintf(stderr, "tint2: launcher %d : %d,%d\n", i, posx, posy);
if (panel_horizontal) {
if (i % icons_per_column) {
posy += launcher->icon_size + launcher->area.paddingx;
posy += launcher->icon_size + launcher->area.paddingx * panel->scale;
} else {
posy = start;
posx += (launcher->icon_size + launcher->area.paddingx);
posx += (launcher->icon_size + launcher->area.paddingx * panel->scale);
}
} else {
if (i % icons_per_row) {
posx += launcher->icon_size + launcher->area.paddingx;
posx += launcher->icon_size + launcher->area.paddingx * panel->scale;
} else {
posx = start;
posy += (launcher->icon_size + launcher->area.paddingx);
posy += (launcher->icon_size + launcher->area.paddingx * panel->scale);
}
}
}
@@ -325,6 +331,17 @@ gboolean resize_launcher(void *obj)
return TRUE;
}
void relayout_launcher(void *obj)
{
Launcher *launcher = (Launcher *)obj;
for (GSList *l = launcher->list_icons; l; l = l->next) {
LauncherIcon *launcherIcon = (LauncherIcon *)l->data;
if (!launcherIcon->area.on_screen)
continue;
launcher_icon_on_change_layout(launcherIcon);
}
}
// Here we override the default layout of the icons; normally Area layouts its children
// in a stack; we need to layout them in a kind of table
void launcher_icon_on_change_layout(void *obj)
@@ -371,7 +388,12 @@ void draw_launcher_icon(void *obj, cairo_t *c)
void launcher_icon_dump_geometry(void *obj, int indent)
{
LauncherIcon *launcherIcon = (LauncherIcon *)obj;
fprintf(stderr, "%*sIcon: w = h = %d, name = %s\n", indent, "", launcherIcon->icon_size, launcherIcon->icon_name);
fprintf(stderr,
"tint2: %*sIcon: w = h = %d, name = %s\n",
indent,
"",
launcherIcon->icon_size,
launcherIcon->icon_name);
}
Imlib_Image scale_icon(Imlib_Image original, int icon_size)
@@ -415,13 +437,26 @@ void free_icon(Imlib_Image icon)
}
}
void launcher_action(LauncherIcon *icon, XEvent *evt)
void launcher_action(LauncherIcon *icon, XEvent *evt, int x, int y)
{
launcher_reload_icon((Launcher *)icon->area.parent, icon);
launcher_reload_hidden_icons((Launcher *)icon->area.parent);
if (evt->type == ButtonPress || evt->type == ButtonRelease)
tint_exec(icon->cmd, icon->cwd, icon->icon_tooltip, evt->xbutton.time);
if (evt->type == ButtonPress || evt->type == ButtonRelease) {
GString *cmd = g_string_new(icon->cmd);
tint2_g_string_replace(cmd, "%f", "");
tint2_g_string_replace(cmd, "%F", "");
tint_exec(cmd->str,
icon->cwd,
icon->icon_tooltip,
evt->xbutton.time,
&icon->area,
x,
y,
icon->start_in_terminal,
icon->startup_notification);
g_string_free(cmd, TRUE);
}
}
// Populates the list_icons list from the list_apps list
@@ -438,7 +473,7 @@ void launcher_load_icons(Launcher *launcher)
launcherIcon->area.size_mode = LAYOUT_FIXED;
launcherIcon->area._resize = NULL;
launcherIcon->area._compute_desired_size = launcher_icon_compute_desired_size;
sprintf(launcherIcon->area.name, "LauncherIcon %d", index);
snprintf(launcherIcon->area.name, sizeof(launcherIcon->area.name), "LauncherIcon %d", index);
launcherIcon->area.resize_needed = 0;
launcherIcon->area.has_mouse_over_effect = panel_config.mouse_effects;
launcherIcon->area.has_mouse_press_effect = launcherIcon->area.has_mouse_over_effect;
@@ -476,6 +511,8 @@ void launcher_reload_icon(Launcher *launcher, LauncherIcon *launcherIcon)
launcherIcon->cwd = strdup(entry.cwd);
else
launcherIcon->cwd = NULL;
launcherIcon->start_in_terminal = entry.start_in_terminal;
launcherIcon->startup_notification = entry.startup_notification;
if (launcherIcon->icon_name)
free(launcherIcon->icon_name);
launcherIcon->icon_name = entry.icon ? strdup(entry.icon) : strdup(DEFAULT_ICON);
@@ -531,7 +568,7 @@ void launcher_reload_icon_image(Launcher *launcher, LauncherIcon *launcherIcon)
free_icon(original);
free(launcherIcon->icon_path);
launcherIcon->icon_path = new_icon_path;
// fprintf(stderr, "launcher.c %d: Using icon %s\n", __LINE__, launcherIcon->icon_path);
// fprintf(stderr, "tint2: launcher.c %d: Using icon %s\n", __LINE__, launcherIcon->icon_path);
if (panel_config.mouse_effects) {
launcherIcon->image_hover = adjust_icon(launcherIcon->image,

View File

@@ -33,6 +33,8 @@ typedef struct LauncherIcon {
Imlib_Image image_pressed;
char *cmd;
char *cwd;
gboolean start_in_terminal;
gboolean startup_notification;
char *icon_name;
char *icon_path;
char *icon_tooltip;
@@ -67,7 +69,7 @@ void launcher_default_icon_theme_changed();
// Populates the list_icons list
void launcher_load_icons(Launcher *launcher);
void launcher_action(LauncherIcon *icon, XEvent *e);
void launcher_action(LauncherIcon *icon, XEvent *e, int x, int y);
void test_launcher_read_desktop_file();
void test_launcher_read_theme_file();

View File

@@ -48,7 +48,7 @@ void xsettings_notify_cb(const char *name, XSettingsAction action, XSettingsSett
{
if ((action == XSETTINGS_ACTION_NEW || action == XSETTINGS_ACTION_CHANGED) && name != NULL && setting != NULL) {
if (strcmp(name, "Net/IconThemeName") == 0 && setting->type == XSETTINGS_TYPE_STRING) {
fprintf(stderr, "xsettings: %s = %s\n", name, setting->data.v_string);
fprintf(stderr, "tint2: xsettings: %s = %s\n", name, setting->data.v_string);
if (icon_theme_name_xsettings) {
if (strcmp(icon_theme_name_xsettings, setting->data.v_string) == 0)
return;
@@ -57,7 +57,7 @@ void xsettings_notify_cb(const char *name, XSettingsAction action, XSettingsSett
icon_theme_name_xsettings = strdup(setting->data.v_string);
default_icon_theme_changed();
} else if (strcmp(name, "Gtk/FontName") == 0 && setting->type == XSETTINGS_TYPE_STRING) {
fprintf(stderr, "xsettings: %s = %s\n", name, setting->data.v_string);
fprintf(stderr, "tint2: xsettings: %s = %s\n", name, setting->data.v_string);
if (default_font) {
if (strcmp(default_font, setting->data.v_string) == 0)
return;
@@ -191,7 +191,7 @@ static XSettingsList *parse_settings(unsigned char *data, size_t len)
result = fetch_card8(&buffer, (CARD8 *)&buffer.byte_order);
if (buffer.byte_order != MSBFirst && buffer.byte_order != LSBFirst) {
fprintf(stderr, "Invalid byte order %x in XSETTINGS property\n", buffer.byte_order);
fprintf(stderr, "tint2: Invalid byte order %x in XSETTINGS property\n", buffer.byte_order);
result = XSETTINGS_FAILED;
goto out;
}
@@ -312,13 +312,13 @@ out:
if (result != XSETTINGS_SUCCESS) {
switch (result) {
case XSETTINGS_NO_MEM:
fprintf(stderr, "Out of memory reading XSETTINGS property\n");
fprintf(stderr, "tint2: Out of memory reading XSETTINGS property\n");
break;
case XSETTINGS_ACCESS:
fprintf(stderr, "Invalid XSETTINGS property (read off end)\n");
fprintf(stderr, "tint2: Invalid XSETTINGS property (read off end)\n");
break;
case XSETTINGS_DUPLICATE_ENTRY:
fprintf(stderr, "Duplicate XSETTINGS entry for '%s'\n", setting->name);
fprintf(stderr, "tint2: Duplicate XSETTINGS entry for '%s'\n", setting->name);
case XSETTINGS_FAILED:
case XSETTINGS_SUCCESS:
case XSETTINGS_NO_ENTRY:
@@ -365,7 +365,7 @@ static void read_settings(XSettingsClient *client)
if (result == Success && type == server.atom._XSETTINGS_SETTINGS) {
if (format != 8) {
fprintf(stderr, "Invalid format for XSETTINGS property %d", format);
fprintf(stderr, "tint2: Invalid format for XSETTINGS property %d", format);
} else
client->settings = parse_settings(data, n_items);
XFree(data);
@@ -420,7 +420,7 @@ XSettingsClient *xsettings_client_new(Display *display,
check_manager_window(client);
if (client->manager_window == None) {
printf("No XSETTINGS manager, tint2 uses config option 'launcher_icon_theme'.\n");
fprintf(stderr, "tint2: No XSETTINGS manager, tint2 uses config option 'launcher_icon_theme'.\n");
free(client);
return NULL;
} else {

806
src/main.c Normal file
View File

@@ -0,0 +1,806 @@
/**************************************************************************
*
* Tint2 panel
*
* Copyright (C) 2007 Pål Staurland (staura@gmail.com)
* Modified (C) 2008 thierry lorthiois (lorthiois@bbsoft.fr) from Omega distribution
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License version 2
* as published by the Free Software Foundation.
*
* This program 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 General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
**************************************************************************/
#include <errno.h>
#include <sys/stat.h>
#include <unistd.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#include <X11/Xutil.h>
#include <X11/Xatom.h>
#include <X11/Xlocale.h>
#include <X11/extensions/Xdamage.h>
#include <Imlib2.h>
#include <signal.h>
#include <unistd.h>
#include <sys/types.h>
#include <pwd.h>
#include <time.h>
#include <sys/time.h>
#include <sys/wait.h>
#ifdef HAVE_SN
#include <libsn/sn.h>
#endif
#include "config.h"
#include "drag_and_drop.h"
#include "fps_distribution.h"
#include "init.h"
#include "launcher.h"
#include "mouse_actions.h"
#include "panel.h"
#include "server.h"
#include "signals.h"
#include "systraybar.h"
#include "task.h"
#include "taskbar.h"
#include "tooltip.h"
#include "timer.h"
#include "tracing.h"
#include "uevent.h"
#include "version.h"
#include "window.h"
#include "xsettings-client.h"
// Global process state variables
XSettingsClient *xsettings_client = NULL;
gboolean debug_fps = FALSE;
gboolean debug_frames = FALSE;
static int frame = 0;
double tracing_fps_threshold = 60;
static double ts_event_read;
static double ts_event_processed;
static double ts_render_finished;
static double ts_flush_finished;
static gboolean first_render;
void handle_event_property_notify(XEvent *e)
{
gboolean debug = FALSE;
Window win = e->xproperty.window;
Atom at = e->xproperty.atom;
if (xsettings_client)
xsettings_client_process_event(xsettings_client, e);
for (int i = 0; i < num_panels; i++) {
Panel *p = &panels[i];
if (win == p->main_win) {
if (at == server.atom._NET_WM_DESKTOP && get_window_desktop(p->main_win) != ALL_DESKTOPS)
replace_panel_all_desktops(p);
return;
}
}
if (win == server.root_win) {
if (!server.got_root_win) {
XSelectInput(server.display, server.root_win, PropertyChangeMask | StructureNotifyMask);
server.got_root_win = TRUE;
}
// Change name of desktops
else if (at == server.atom._NET_DESKTOP_NAMES) {
if (debug)
fprintf(stderr, "tint2: %s %d: win = root, atom = _NET_DESKTOP_NAMES\n", __func__, __LINE__);
update_desktop_names();
}
// Change desktops
else if (at == server.atom._NET_NUMBER_OF_DESKTOPS || at == server.atom._NET_DESKTOP_GEOMETRY ||
at == server.atom._NET_DESKTOP_VIEWPORT || at == server.atom._NET_WORKAREA ||
at == server.atom._NET_CURRENT_DESKTOP) {
if (debug)
fprintf(stderr, "tint2: %s %d: win = root, atom = ?? desktops changed\n", __func__, __LINE__);
if (!taskbar_enabled)
return;
int old_num_desktops = server.num_desktops;
int old_desktop = server.desktop;
server_get_number_of_desktops();
server.desktop = get_current_desktop();
if (old_num_desktops != server.num_desktops) { // If number of desktop changed
if (server.num_desktops <= server.desktop) {
server.desktop = server.num_desktops - 1;
}
cleanup_taskbar();
init_taskbar();
for (int i = 0; i < num_panels; i++) {
init_taskbar_panel(&panels[i]);
set_panel_items_order(&panels[i]);
panels[i].area.resize_needed = 1;
}
taskbar_refresh_tasklist();
reset_active_task();
update_all_taskbars_visibility();
if (old_desktop != server.desktop)
tooltip_trigger_hide();
schedule_panel_redraw();
} else if (old_desktop != server.desktop) {
tooltip_trigger_hide();
for (int i = 0; i < num_panels; i++) {
Panel *panel = &panels[i];
set_taskbar_state(&panel->taskbar[old_desktop], TASKBAR_NORMAL);
set_taskbar_state(&panel->taskbar[server.desktop], TASKBAR_ACTIVE);
// check ALL_DESKTOPS task => resize taskbar
Taskbar *taskbar;
if (server.num_desktops > old_desktop) {
taskbar = &panel->taskbar[old_desktop];
GList *l = taskbar->area.children;
if (taskbarname_enabled)
l = l->next;
for (; l; l = l->next) {
Task *task = l->data;
if (task->desktop == ALL_DESKTOPS) {
task->area.on_screen = always_show_all_desktop_tasks;
taskbar->area.resize_needed = 1;
schedule_panel_redraw();
if (taskbar_mode == MULTI_DESKTOP)
panel->area.resize_needed = 1;
}
}
}
taskbar = &panel->taskbar[server.desktop];
GList *l = taskbar->area.children;
if (taskbarname_enabled)
l = l->next;
for (; l; l = l->next) {
Task *task = l->data;
if (task->desktop == ALL_DESKTOPS) {
task->area.on_screen = TRUE;
taskbar->area.resize_needed = 1;
if (taskbar_mode == MULTI_DESKTOP)
panel->area.resize_needed = 1;
}
}
if (server.viewports) {
GList *need_update = NULL;
GHashTableIter iter;
gpointer key, value;
g_hash_table_iter_init(&iter, win_to_task);
while (g_hash_table_iter_next(&iter, &key, &value)) {
Window task_win = *(Window *)key;
Task *task = get_task(task_win);
if (task) {
int desktop = get_window_desktop(task_win);
if (desktop != task->desktop) {
need_update = g_list_append(need_update, task);
}
}
}
for (l = need_update; l; l = l->next) {
Task *task = l->data;
task_update_desktop(task);
}
}
}
}
}
// Window list
else if (at == server.atom._NET_CLIENT_LIST) {
if (debug)
fprintf(stderr, "tint2: %s %d: win = root, atom = _NET_CLIENT_LIST\n", __func__, __LINE__);
taskbar_refresh_tasklist();
update_all_taskbars_visibility();
schedule_panel_redraw();
}
// Change active
else if (at == server.atom._NET_ACTIVE_WINDOW) {
if (debug)
fprintf(stderr, "tint2: %s %d: win = root, atom = _NET_ACTIVE_WINDOW\n", __func__, __LINE__);
reset_active_task();
schedule_panel_redraw();
} else if (at == server.atom._XROOTPMAP_ID || at == server.atom._XROOTMAP_ID) {
if (debug)
fprintf(stderr, "tint2: %s %d: win = root, atom = _XROOTPMAP_ID\n", __func__, __LINE__);
// change Wallpaper
for (int i = 0; i < num_panels; i++) {
set_panel_background(&panels[i]);
}
schedule_panel_redraw();
}
} else {
TrayWindow *traywin = systray_find_icon(win);
if (traywin) {
systray_property_notify(traywin, e);
return;
}
Task *task = get_task(win);
if (debug) {
char *atom_name = XGetAtomName(server.display, at);
fprintf(stderr,
"%s %d: win = %ld, task = %s, atom = %s\n",
__func__,
__LINE__,
win,
task ? (task->title ? task->title : "??") : "null",
atom_name);
XFree(atom_name);
}
if (!task) {
if (debug)
fprintf(stderr, "tint2: %s %d\n", __func__, __LINE__);
if (at == server.atom._NET_WM_STATE) {
// xfce4 sends _NET_WM_STATE after minimized to tray, so we need to check if window is mapped
// if it is mapped and not set as skip_taskbar, we must add it to our task list
XWindowAttributes wa;
XGetWindowAttributes(server.display, win, &wa);
if (wa.map_state == IsViewable && !window_is_skip_taskbar(win)) {
if ((task = add_task(win)))
schedule_panel_redraw();
}
}
return;
}
// fprintf(stderr, "tint2: atom root_win = %s, %s\n", XGetAtomName(server.display, at), task->title);
// Window title changed
if (at == server.atom._NET_WM_VISIBLE_NAME || at == server.atom._NET_WM_NAME || at == server.atom.WM_NAME) {
if (task_update_title(task)) {
if (g_tooltip.mapped && (g_tooltip.area == (Area *)task)) {
tooltip_update_contents_for((Area *)task);
tooltip_update();
}
if (taskbar_sort_method == TASKBAR_SORT_TITLE)
sort_taskbar_for_win(win);
schedule_panel_redraw();
}
}
// Demand attention
else if (at == server.atom._NET_WM_STATE) {
if (debug) {
int count;
Atom *atom_state = server_get_property(win, server.atom._NET_WM_STATE, XA_ATOM, &count);
for (int j = 0; j < count; j++) {
char *atom_state_name = XGetAtomName(server.display, atom_state[j]);
fprintf(stderr, "tint2: %s %d: _NET_WM_STATE = %s\n", __func__, __LINE__, atom_state_name);
XFree(atom_state_name);
}
XFree(atom_state);
}
if (window_is_urgent(win)) {
add_urgent(task);
}
if (window_is_skip_taskbar(win)) {
remove_task(task);
schedule_panel_redraw();
}
} else if (at == server.atom.WM_STATE) {
// Iconic state
TaskState state = (active_task && task->win == active_task->win ? TASK_ACTIVE : TASK_NORMAL);
if (window_is_iconified(win))
state = TASK_ICONIFIED;
set_task_state(task, state);
schedule_panel_redraw();
}
// Window icon changed
else if (at == server.atom._NET_WM_ICON) {
task_update_icon(task);
schedule_panel_redraw();
}
// Window desktop changed
else if (at == server.atom._NET_WM_DESKTOP) {
int desktop = get_window_desktop(win);
// fprintf(stderr, "tint2: Window desktop changed %d, %d\n", task->desktop, desktop);
// bug in windowmaker : send unecessary 'desktop changed' when focus changed
if (desktop != task->desktop) {
task_update_desktop(task);
}
} else if (at == server.atom.WM_HINTS) {
XWMHints *wmhints = XGetWMHints(server.display, win);
if (wmhints && wmhints->flags & XUrgencyHint) {
add_urgent(task);
}
XFree(wmhints);
task_update_icon(task);
schedule_panel_redraw();
}
if (!server.got_root_win)
server.root_win = RootWindow(server.display, server.screen);
}
}
void handle_event_expose(XEvent *e)
{
Panel *panel;
panel = get_panel(e->xany.window);
if (!panel)
return;
// TODO : one panel_refresh per panel ?
schedule_panel_redraw();
}
void handle_event_configure_notify(XEvent *e)
{
Window win = e->xconfigure.window;
// change in root window (xrandr)
if (win == server.root_win) {
emit_self_restart("configuration change in the root window");
return;
}
TrayWindow *traywin = systray_find_icon(win);
if (traywin) {
systray_reconfigure_event(traywin, e);
return;
}
// 'win' move in another monitor
if (num_panels > 1 || hide_task_diff_monitor) {
Task *task = get_task(win);
if (task) {
Panel *p = task->area.panel;
int monitor = get_window_monitor(win);
if ((hide_task_diff_monitor && p->monitor != monitor && task->area.on_screen) ||
(hide_task_diff_monitor && p->monitor == monitor && !task->area.on_screen) ||
(p->monitor != monitor && num_panels > 1)) {
remove_task(task);
task = add_task(win);
if (win == get_active_window()) {
set_task_state(task, TASK_ACTIVE);
active_task = task;
}
schedule_panel_redraw();
}
}
}
if (server.viewports) {
Task *task = get_task(win);
if (task) {
int desktop = get_window_desktop(win);
if (task->desktop != desktop) {
task_update_desktop(task);
}
}
}
sort_taskbar_for_win(win);
}
gboolean handle_x_event_autohide(XEvent *e)
{
Panel *panel = get_panel(e->xany.window);
if (panel && panel_autohide) {
if (e->type == EnterNotify)
autohide_trigger_show(panel);
else if (e->type == LeaveNotify)
autohide_trigger_hide(panel);
if (panel->is_hidden) {
if (e->type == ClientMessage && e->xclient.message_type == server.atom.XdndPosition) {
hidden_panel_shown_for_dnd = TRUE;
autohide_show(panel);
} else {
// discard further processing of this event because the panel is not visible yet
return TRUE;
}
} else if (hidden_panel_shown_for_dnd && e->type == ClientMessage &&
e->xclient.message_type == server.atom.XdndLeave) {
hidden_panel_shown_for_dnd = FALSE;
autohide_hide(panel);
}
}
return FALSE;
}
void handle_x_event(XEvent *e)
{
#if HAVE_SN
if (startup_notifications)
sn_display_process_event(server.sn_display, e);
#endif // HAVE_SN
if (handle_x_event_autohide(e))
return;
Panel *panel = get_panel(e->xany.window);
switch (e->type) {
case ButtonPress: {
tooltip_hide(0);
handle_mouse_press_event(e);
Area *area = find_area_under_mouse(panel, e->xbutton.x, e->xbutton.y);
if (panel_config.mouse_effects)
mouse_over(area, TRUE);
break;
}
case ButtonRelease: {
handle_mouse_release_event(e);
Area *area = find_area_under_mouse(panel, e->xbutton.x, e->xbutton.y);
if (panel_config.mouse_effects)
mouse_over(area, FALSE);
break;
}
case MotionNotify: {
unsigned int button_mask = Button1Mask | Button2Mask | Button3Mask | Button4Mask | Button5Mask;
if (e->xmotion.state & button_mask)
handle_mouse_move_event(e);
Area *area = find_area_under_mouse(panel, e->xmotion.x, e->xmotion.y);
if (area->_get_tooltip_text)
tooltip_trigger_show(area, panel, e);
else
tooltip_trigger_hide();
if (panel_config.mouse_effects)
mouse_over(area, e->xmotion.state & button_mask);
break;
}
case LeaveNotify: {
tooltip_trigger_hide();
if (panel_config.mouse_effects)
mouse_out();
break;
}
case Expose:
handle_event_expose(e);
break;
case PropertyNotify:
handle_event_property_notify(e);
break;
case ConfigureNotify:
handle_event_configure_notify(e);
break;
case ConfigureRequest: {
TrayWindow *traywin = systray_find_icon(e->xany.window);
if (traywin)
systray_reconfigure_event(traywin, e);
break;
}
case ResizeRequest: {
TrayWindow *traywin = systray_find_icon(e->xany.window);
if (traywin)
systray_resize_request_event(traywin, e);
break;
}
case ReparentNotify: {
if (!systray_enabled)
break;
Panel *systray_panel = (Panel *)systray.area.panel;
if (e->xany.window == systray_panel->main_win) // don't care
break;
TrayWindow *traywin = systray_find_icon(e->xreparent.window);
if (traywin) {
if (traywin->win == e->xreparent.window) {
if (traywin->parent == e->xreparent.parent) {
embed_icon(traywin);
} else {
remove_icon(traywin);
}
break;
}
}
break;
}
case UnmapNotify:
break;
case DestroyNotify:
if (e->xany.window == server.composite_manager) {
// Stop real_transparency
emit_self_restart("compositor shutdown");
break;
}
if (e->xany.window == g_tooltip.window || !systray_enabled)
break;
for (GSList *it = systray.list_icons; it; it = g_slist_next(it)) {
if (((TrayWindow *)it->data)->win == e->xany.window) {
systray_destroy_event((TrayWindow *)it->data);
break;
}
}
break;
case ClientMessage: {
XClientMessageEvent *ev = &e->xclient;
if (ev->data.l[1] == server.atom._NET_WM_CM_S0) {
if (ev->data.l[2] == None) {
// Stop real_transparency
emit_self_restart("compositor changed");
} else {
// Start real_transparency
emit_self_restart("compositor changed");
}
}
if (systray_enabled && e->xclient.message_type == server.atom._NET_SYSTEM_TRAY_OPCODE &&
e->xclient.format == 32 && e->xclient.window == net_sel_win) {
handle_systray_event(&e->xclient);
} else if (e->xclient.message_type == server.atom.XdndEnter) {
handle_dnd_enter(&e->xclient);
} else if (e->xclient.message_type == server.atom.XdndPosition) {
handle_dnd_position(&e->xclient);
} else if (e->xclient.message_type == server.atom.XdndDrop) {
handle_dnd_drop(&e->xclient);
}
break;
}
case SelectionNotify: {
handle_dnd_selection_notify(&e->xselection);
break;
}
default:
if (e->type == server.xdamage_event_type) {
XDamageNotifyEvent *de = (XDamageNotifyEvent *)e;
TrayWindow *traywin = systray_find_icon(de->drawable);
if (traywin)
systray_render_icon(traywin);
}
}
}
void handle_x_events()
{
if (XPending(server.display) > 0) {
XEvent e;
XNextEvent(server.display, &e);
if (debug_fps)
ts_event_read = get_time();
handle_x_event(&e);
}
}
void prepare_fd_set(fd_set *set, int *max_fd)
{
FD_ZERO(set);
FD_SET(server.x11_fd, set);
*max_fd = server.x11_fd;
if (sigchild_pipe_valid) {
FD_SET(sigchild_pipe[0], set);
*max_fd = MAX(*max_fd, sigchild_pipe[0]);
}
for (GList *l = panel_config.execp_list; l; l = l->next) {
Execp *execp = (Execp *)l->data;
int fd = execp->backend->child_pipe_stdout;
if (fd > 0) {
FD_SET(fd, set);
*max_fd = MAX(*max_fd, fd);
}
fd = execp->backend->child_pipe_stderr;
if (fd > 0) {
FD_SET(fd, set);
*max_fd = MAX(*max_fd, fd);
}
}
if (uevent_fd > 0) {
FD_SET(uevent_fd, set);
*max_fd = MAX(*max_fd, uevent_fd);
}
}
void handle_panel_refresh()
{
if (debug_fps)
ts_event_processed = get_time();
panel_refresh = FALSE;
for (int i = 0; i < num_panels; i++) {
Panel *panel = &panels[i];
if (!first_render)
shrink_panel(panel);
if (!panel->is_hidden || panel->area.resize_needed) {
if (panel->temp_pmap)
XFreePixmap(server.display, panel->temp_pmap);
panel->temp_pmap = XCreatePixmap(server.display,
server.root_win,
panel->area.width,
panel->area.height,
server.depth);
render_panel(panel);
}
if (panel->is_hidden) {
if (!panel->hidden_pixmap) {
panel->hidden_pixmap = XCreatePixmap(server.display,
server.root_win,
panel->hidden_width,
panel->hidden_height,
server.depth);
int xoff = 0, yoff = 0;
if (panel_horizontal && panel_position & BOTTOM)
yoff = panel->area.height - panel->hidden_height;
else if (!panel_horizontal && panel_position & RIGHT)
xoff = panel->area.width - panel->hidden_width;
XCopyArea(server.display,
panel->area.pix,
panel->hidden_pixmap,
server.gc,
xoff,
yoff,
panel->hidden_width,
panel->hidden_height,
0,
0);
}
XCopyArea(server.display,
panel->hidden_pixmap,
panel->main_win,
server.gc,
0,
0,
panel->hidden_width,
panel->hidden_height,
0,
0);
XSetWindowBackgroundPixmap(server.display, panel->main_win, panel->hidden_pixmap);
} else {
XCopyArea(server.display,
panel->temp_pmap,
panel->main_win,
server.gc,
0,
0,
panel->area.width,
panel->area.height,
0,
0);
if (panel == (Panel *)systray.area.panel) {
if (refresh_systray && panel && !panel->is_hidden) {
refresh_systray = FALSE;
XSetWindowBackgroundPixmap(server.display, panel->main_win, panel->temp_pmap);
refresh_systray_icons();
}
}
}
}
if (first_render) {
first_render = FALSE;
if (panel_shrink)
schedule_panel_redraw();
}
if (debug_fps)
ts_render_finished = get_time();
XFlush(server.display);
if (debug_fps && ts_event_read > 0) {
ts_flush_finished = get_time();
double period = ts_flush_finished - ts_event_read;
double fps = 1.0 / period;
sample_fps(fps);
double proc_ratio = (ts_event_processed - ts_event_read) / period;
double render_ratio = (ts_render_finished - ts_event_processed) / period;
double flush_ratio = (ts_flush_finished - ts_render_finished) / period;
double fps_low, fps_median, fps_high, fps_samples;
fps_compute_stats(&fps_low, &fps_median, &fps_high, &fps_samples);
fprintf(stderr,
BLUE "frame %d: fps = %.0f (low %.0f, med %.0f, high %.0f, samples %.0f) : processing %.0f%%, "
"rendering %.0f%%, "
"flushing %.0f%%" RESET "\n",
frame,
fps,
fps_low,
fps_median,
fps_high,
fps_samples,
proc_ratio * 100,
render_ratio * 100,
flush_ratio * 100);
#ifdef HAVE_TRACING
stop_tracing();
if (fps <= tracing_fps_threshold) {
print_tracing_events();
}
#endif
}
if (debug_frames) {
for (int i = 0; i < num_panels; i++) {
char path[256];
snprintf(path, sizeof(path), "tint2-%d-panel-%d-frame-%d.png", getpid(), i, frame);
save_panel_screenshot(&panels[i], path);
}
}
frame++;
}
void run_tint2_event_loop()
{
ts_event_read = 0;
ts_event_processed = 0;
ts_render_finished = 0;
ts_flush_finished = 0;
first_render = TRUE;
while (!get_signal_pending()) {
if (panel_refresh)
handle_panel_refresh();
fd_set fds;
int max_fd;
prepare_fd_set(&fds, &max_fd);
// Wait for an event and handle it
ts_event_read = 0;
if (XPending(server.display) > 0 || select(max_fd + 1, &fds, 0, 0, get_duration_to_next_timer_expiration()) >= 0) {
#ifdef HAVE_TRACING
start_tracing((void*)run_tint2_event_loop);
#endif
uevent_handler();
handle_sigchld_events();
handle_execp_events();
handle_x_events();
}
handle_expired_timers();
}
}
void tint2(int argc, char **argv, gboolean *restart)
{
init(argc, argv);
if (snapshot_path) {
save_screenshot(snapshot_path);
cleanup();
return;
}
dnd_init();
uevent_init();
run_tint2_event_loop();
if (get_signal_pending()) {
cleanup();
if (get_signal_pending() == SIGUSR1) {
fprintf(stderr, YELLOW "tint2: %s %d: restarting tint2..." RESET "\n", __FILE__, __LINE__);
*restart = TRUE;
return;
} else if (get_signal_pending() == SIGUSR2) {
fprintf(stderr, YELLOW "tint2: %s %d: reexecuting tint2..." RESET "\n", __FILE__, __LINE__);
if (execvp(argv[0], argv) == -1) {
fprintf(stderr, RED "tint2: %s %d: failed!" RESET "\n", __FILE__, __LINE__);
return;
}
} else {
// SIGINT, SIGTERM, SIGHUP etc.
return;
}
}
}
int main(int argc, char **argv)
{
gboolean restart;
do {
restart = FALSE;
tint2(argc, argv, &restart);
} while(restart);
return 0;
}

287
src/mouse_actions.c Normal file
View File

@@ -0,0 +1,287 @@
/**************************************************************************
* Copyright (C) 2017 tint2 authors
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License version 2
* as published by the Free Software Foundation.
*
* This program 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 General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
**************************************************************************/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "drag_and_drop.h"
#include "panel.h"
#include "server.h"
#include "task.h"
#include "window.h"
gboolean tint2_handles_click(Panel *panel, XButtonEvent *e)
{
Task *task = click_task(panel, e->x, e->y);
if (task) {
if ((e->button == 1 && mouse_left != 0) || (e->button == 2 && mouse_middle != 0) ||
(e->button == 3 && mouse_right != 0) || (e->button == 4 && mouse_scroll_up != 0) ||
(e->button == 5 && mouse_scroll_down != 0)) {
return TRUE;
} else
return FALSE;
}
LauncherIcon *icon = click_launcher_icon(panel, e->x, e->y);
if (icon) {
if (e->button == 1) {
return TRUE;
} else {
return FALSE;
}
}
// no launcher/task clicked --> check if taskbar clicked
Taskbar *taskbar = click_taskbar(panel, e->x, e->y);
if (taskbar && e->button == 1 && taskbar_mode == MULTI_DESKTOP)
return TRUE;
if (click_clock(panel, e->x, e->y)) {
if ((e->button == 1 && clock_lclick_command) || (e->button == 2 && clock_mclick_command) ||
(e->button == 3 && clock_rclick_command) || (e->button == 4 && clock_uwheel_command) ||
(e->button == 5 && clock_dwheel_command))
return TRUE;
else
return FALSE;
}
#ifdef ENABLE_BATTERY
if (click_battery(panel, e->x, e->y)) {
if ((e->button == 1 && battery_lclick_command) || (e->button == 2 && battery_mclick_command) ||
(e->button == 3 && battery_rclick_command) || (e->button == 4 && battery_uwheel_command) ||
(e->button == 5 && battery_dwheel_command))
return TRUE;
else
return FALSE;
}
#endif
if (click_execp(panel, e->x, e->y))
return TRUE;
if (click_button(panel, e->x, e->y))
return TRUE;
return FALSE;
}
void handle_mouse_press_event(XEvent *e)
{
Panel *panel = get_panel(e->xany.window);
if (!panel)
return;
if (wm_menu && !tint2_handles_click(panel, &e->xbutton)) {
forward_click(e);
return;
}
task_drag = click_task(panel, e->xbutton.x, e->xbutton.y);
if (panel_layer == BOTTOM_LAYER)
XLowerWindow(server.display, panel->main_win);
}
void handle_mouse_move_event(XEvent *e)
{
Panel *panel = get_panel(e->xany.window);
if (!panel || !task_drag)
return;
// Find the taskbar on the event's location
Taskbar *event_taskbar = click_taskbar(panel, e->xbutton.x, e->xbutton.y);
if (event_taskbar == NULL)
return;
// Find the task on the event's location
Task *event_task = click_task(panel, e->xbutton.x, e->xbutton.y);
// If the event takes place on the same taskbar as the task being dragged
if (&event_taskbar->area == task_drag->area.parent) {
if (taskbar_sort_method != TASKBAR_NOSORT) {
sort_tasks(event_taskbar);
} else {
// Swap the task_drag with the task on the event's location (if they differ)
if (event_task && event_task != task_drag) {
GList *drag_iter = g_list_find(event_taskbar->area.children, task_drag);
GList *task_iter = g_list_find(event_taskbar->area.children, event_task);
if (drag_iter && task_iter) {
gpointer temp = task_iter->data;
task_iter->data = drag_iter->data;
drag_iter->data = temp;
event_taskbar->area.resize_needed = 1;
schedule_panel_redraw();
task_dragged = 1;
}
}
}
} else { // The event is on another taskbar than the task being dragged
if (task_drag->desktop == ALL_DESKTOPS || taskbar_mode != MULTI_DESKTOP)
return;
Taskbar *drag_taskbar = (Taskbar *)task_drag->area.parent;
remove_area((Area *)task_drag);
if (event_taskbar->area.posx > drag_taskbar->area.posx || event_taskbar->area.posy > drag_taskbar->area.posy) {
int i = (taskbarname_enabled) ? 1 : 0;
event_taskbar->area.children = g_list_insert(event_taskbar->area.children, task_drag, i);
} else
event_taskbar->area.children = g_list_append(event_taskbar->area.children, task_drag);
// Move task to other desktop (but avoid the 'Window desktop changed' code in 'event_property_notify')
task_drag->area.parent = &event_taskbar->area;
task_drag->desktop = event_taskbar->desktop;
change_window_desktop(task_drag->win, event_taskbar->desktop);
if (hide_task_diff_desktop)
change_desktop(event_taskbar->desktop);
if (taskbar_sort_method != TASKBAR_NOSORT) {
sort_tasks(event_taskbar);
}
event_taskbar->area.resize_needed = 1;
drag_taskbar->area.resize_needed = 1;
task_dragged = 1;
schedule_panel_redraw();
panel->area.resize_needed = 1;
}
}
void handle_mouse_release_event(XEvent *e)
{
Panel *panel = get_panel(e->xany.window);
if (!panel)
return;
if (wm_menu && !tint2_handles_click(panel, &e->xbutton)) {
forward_click(e);
if (panel_layer == BOTTOM_LAYER)
XLowerWindow(server.display, panel->main_win);
task_drag = 0;
return;
}
MouseAction action = TOGGLE_ICONIFY;
switch (e->xbutton.button) {
case 1:
action = mouse_left;
break;
case 2:
action = mouse_middle;
break;
case 3:
action = mouse_right;
break;
case 4:
action = mouse_scroll_up;
break;
case 5:
action = mouse_scroll_down;
break;
case 6:
action = mouse_tilt_left;
break;
case 7:
action = mouse_tilt_right;
break;
}
Clock *clock = click_clock(panel, e->xbutton.x, e->xbutton.y);
if (clock) {
clock_action(clock, e->xbutton.button, e->xbutton.x - clock->area.posx, e->xbutton.y - clock->area.posy, e->xbutton.time);
if (panel_layer == BOTTOM_LAYER)
XLowerWindow(server.display, panel->main_win);
task_drag = 0;
return;
}
#ifdef ENABLE_BATTERY
Battery *battery = click_battery(panel, e->xbutton.x, e->xbutton.y);
if (battery) {
battery_action(battery, e->xbutton.button, e->xbutton.x - battery->area.posx, e->xbutton.y - battery->area.posy, e->xbutton.time);
if (panel_layer == BOTTOM_LAYER)
XLowerWindow(server.display, panel->main_win);
task_drag = 0;
return;
}
#endif
Execp *execp = click_execp(panel, e->xbutton.x, e->xbutton.y);
if (execp) {
execp_action(execp, e->xbutton.button, e->xbutton.x - execp->area.posx, e->xbutton.y - execp->area.posy, e->xbutton.time);
if (panel_layer == BOTTOM_LAYER)
XLowerWindow(server.display, panel->main_win);
task_drag = 0;
return;
}
Button *button = click_button(panel, e->xbutton.x, e->xbutton.y);
if (button) {
button_action(button, e->xbutton.button, e->xbutton.x - button->area.posx, e->xbutton.y - button->area.posy, e->xbutton.time);
if (panel_layer == BOTTOM_LAYER)
XLowerWindow(server.display, panel->main_win);
task_drag = 0;
return;
}
if (e->xbutton.button == 1 && click_launcher(panel, e->xbutton.x, e->xbutton.y)) {
LauncherIcon *icon = click_launcher_icon(panel, e->xbutton.x, e->xbutton.y);
if (icon) {
launcher_action(icon, e, e->xbutton.x - icon->area.posx, e->xbutton.y - icon->area.posy);
}
task_drag = 0;
return;
}
Taskbar *taskbar;
if (!(taskbar = click_taskbar(panel, e->xbutton.x, e->xbutton.y))) {
// TODO: check better solution to keep window below
if (panel_layer == BOTTOM_LAYER)
XLowerWindow(server.display, panel->main_win);
task_drag = 0;
return;
}
// drag and drop task
if (task_dragged) {
task_drag = 0;
task_dragged = 0;
return;
}
// switch desktop
if (taskbar_mode == MULTI_DESKTOP) {
gboolean diff_desktop = FALSE;
if (taskbar->desktop != server.desktop && action != CLOSE && action != DESKTOP_LEFT &&
action != DESKTOP_RIGHT) {
diff_desktop = TRUE;
change_desktop(taskbar->desktop);
}
Task *task = click_task(panel, e->xbutton.x, e->xbutton.y);
if (task) {
if (diff_desktop) {
if (action == TOGGLE_ICONIFY) {
if (!window_is_active(task->win))
activate_window(task->win);
} else {
task_handle_mouse_event(task, action);
}
} else {
task_handle_mouse_event(task, action);
}
}
} else {
task_handle_mouse_event(click_task(panel, e->xbutton.x, e->xbutton.y), action);
}
// to keep window below
if (panel_layer == BOTTOM_LAYER)
XLowerWindow(server.display, panel->main_win);
}

12
src/mouse_actions.h Normal file
View File

@@ -0,0 +1,12 @@
#ifndef MOUSE_ACTIONS_H
#define MOUSE_ACTIONS_H
#include "panel.h"
gboolean tint2_handles_click(Panel *panel, XButtonEvent *e);
void handle_mouse_press_event(XEvent *e);
void handle_mouse_move_event(XEvent *e);
void handle_mouse_release_event(XEvent *e);
#endif

View File

@@ -20,6 +20,8 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <unistd.h>
#include <X11/Xlib.h>
#include <X11/Xutil.h>
#include <X11/Xatom.h>
@@ -36,8 +38,6 @@
void panel_clear_background(void *obj);
int signal_pending;
MouseAction mouse_left;
MouseAction mouse_middle;
MouseAction mouse_right;
@@ -58,6 +58,7 @@ char *panel_window_name = NULL;
gboolean debug_geometry;
gboolean debug_gradients;
gboolean startup_notifications;
gboolean debug_thumbnails;
gboolean panel_autohide;
int panel_autohide_show_timeout;
@@ -78,11 +79,16 @@ int num_panels;
GArray *backgrounds;
GArray *gradients;
double ui_scale_dpi_ref;
double ui_scale_monitor_size_ref;
Imlib_Image default_icon;
char *default_font = NULL;
void default_panel()
{
ui_scale_dpi_ref = 0;
ui_scale_monitor_size_ref = 0;
panels = NULL;
num_panels = 0;
default_icon = NULL;
@@ -129,8 +135,6 @@ void cleanup_panel()
if (!panels)
return;
cleanup_taskbar();
for (int i = 0; i < num_panels; i++) {
Panel *p = &panels[i];
@@ -144,10 +148,12 @@ void cleanup_panel()
if (p->main_win)
XDestroyWindow(server.display, p->main_win);
p->main_win = 0;
stop_timeout(p->autohide_timeout);
destroy_timer(&p->autohide_timer);
cleanup_freespace(p);
}
free_icon_themes();
free(panel_items_order);
panel_items_order = NULL;
free(panel_window_name);
@@ -175,11 +181,11 @@ void init_panel()
{
if (panel_config.monitor > (server.num_monitors - 1)) {
// server.num_monitors minimum value is 1 (see get_monitors())
fprintf(stderr, "warning : monitor not found. tint2 default to all monitors.\n");
fprintf(stderr, "tint2: warning : monitor not found. tint2 default to all monitors.\n");
panel_config.monitor = 0;
}
fprintf(stderr, "panel items: %s\n", panel_items_order);
fprintf(stderr, "tint2: panel items: %s\n", panel_items_order);
icon_theme_wrapper = NULL;
@@ -204,10 +210,11 @@ void init_panel()
panels = calloc(num_panels, sizeof(Panel));
for (int i = 0; i < num_panels; i++) {
memcpy(&panels[i], &panel_config, sizeof(Panel));
INIT_TIMER(panels[i].autohide_timer);
}
fprintf(stderr,
"tint2 : nb monitor %d, nb monitor used %d, nb desktop %d\n",
"tint2: nb monitors %d, nb monitors used %d, nb desktops %d\n",
server.num_monitors,
num_panels,
server.num_desktops);
@@ -216,6 +223,17 @@ void init_panel()
if (panel_config.monitor < 0)
p->monitor = i;
if (ui_scale_dpi_ref > 0 && server.monitors[p->monitor].dpi > 0)
p->scale = server.monitors[p->monitor].dpi / ui_scale_dpi_ref;
else
p->scale = 1;
if (ui_scale_monitor_size_ref > 0)
p->scale *= server.monitors[p->monitor].height / ui_scale_monitor_size_ref;
if (p->scale > 8 || p->scale < 1./8) {
fprintf(stderr, RED "tint2: panel %d having scale %g outside bounds, resetting to 1.0" RESET "\n", i + 1, p->scale);
p->scale = 1;
}
fprintf(stderr, BLUE "tint2: panel %d uses scale %g " RESET "\n", i + 1, p->scale);
if (!p->area.bg)
p->area.bg = &g_array_index(backgrounds, Background, 0);
p->area.parent = p;
@@ -287,7 +305,7 @@ void init_panel()
XGCValues gcv;
server.gc = XCreateGC(server.display, p->main_win, 0, &gcv);
}
// printf("panel %d : %d, %d, %d, %d\n", i, p->posx, p->posy, p->area.width, p->area.height);
// fprintf(stderr, "tint2: panel %d : %d, %d, %d, %d\n", i, p->posx, p->posy, p->area.width, p->area.height);
set_panel_properties(p);
set_panel_background(p);
if (!snapshot_path) {
@@ -320,7 +338,7 @@ void panel_compute_size(Panel *panel)
if (panel->fractional_height)
panel->area.height = (server.monitors[panel->monitor].height - panel->marginy) * panel->area.height / 100;
if (panel->area.bg->border.radius > panel->area.height / 2) {
printf("panel_background_id rounded is too big... please fix your tint2rc\n");
fprintf(stderr, "tint2: panel_background_id rounded is too big... please fix your tint2rc\n");
g_array_append_val(backgrounds, *panel->area.bg);
panel->area.bg = &g_array_index(backgrounds, Background, backgrounds->len - 1);
panel->area.bg->border.radius = panel->area.height / 2;
@@ -346,13 +364,26 @@ void panel_compute_size(Panel *panel)
panel->area.width = old_panel_height;
if (panel->area.bg->border.radius > panel->area.width / 2) {
printf("panel_background_id rounded is too big... please fix your tint2rc\n");
fprintf(stderr, "tint2: panel_background_id rounded is too big... please fix your tint2rc\n");
g_array_append_val(backgrounds, *panel->area.bg);
panel->area.bg = &g_array_index(backgrounds, Background, backgrounds->len - 1);
panel->area.bg->border.radius = panel->area.width / 2;
}
}
if (!panel->fractional_width) {
if (panel_horizontal)
panel->area.width *= panel->scale;
else
panel->area.height *= panel->scale;
}
if (!panel->fractional_height) {
if (panel_horizontal)
panel->area.height *= panel->scale;
else
panel->area.width *= panel->scale;
}
if (panel->area.width + panel->marginx > server.monitors[panel->monitor].width)
panel->area.width = server.monitors[panel->monitor].width - panel->marginx;
if (panel->area.height + panel->marginy > server.monitors[panel->monitor].height)
@@ -399,7 +430,7 @@ void panel_compute_position(Panel *panel)
panel->hidden_width = panel->area.width - diff;
panel->hidden_height = panel->area.height;
}
// printf("panel : posx %d, posy %d, width %d, height %d\n", panel->posx, panel->posy, panel->area.width,
// fprintf(stderr, "tint2: panel : posx %d, posy %d, width %d, height %d\n", panel->posx, panel->posy, panel->area.width,
// panel->area.height);
}
@@ -414,7 +445,7 @@ gboolean resize_panel(void *obj)
Panel *panel = (Panel *)obj;
relayout_with_constraint(&panel->area, 0);
// printf("resize_panel\n");
// fprintf(stderr, "tint2: resize_panel\n");
if (taskbar_mode != MULTI_DESKTOP && taskbar_enabled) {
// propagate width/height on hidden taskbar
int width = panel->taskbar[server.desktop].area.width;
@@ -447,9 +478,9 @@ gboolean resize_panel(void *obj)
if (!taskbar->area.on_screen)
continue;
if (panel_horizontal)
taskbar->area.width = 2 * taskbar->area.paddingxlr;
taskbar->area.width = 2 * taskbar->area.paddingxlr * panel->scale;
else
taskbar->area.height = 2 * taskbar->area.paddingxlr;
taskbar->area.height = 2 * taskbar->area.paddingxlr * panel->scale;
if (taskbarname_enabled && taskbar->area.children) {
Area *name = (Area *)taskbar->area.children->data;
if (name->on_screen) {
@@ -466,9 +497,9 @@ gboolean resize_panel(void *obj)
continue;
if (!first_child) {
if (panel_horizontal)
taskbar->area.width += taskbar->area.paddingx;
taskbar->area.width += taskbar->area.paddingx * panel->scale;
else
taskbar->area.height += taskbar->area.paddingy;
taskbar->area.height += taskbar->area.paddingy * panel->scale;
}
first_child = FALSE;
}
@@ -491,9 +522,11 @@ gboolean resize_panel(void *obj)
}
}
// Distribute the remaining size between tasks
// Distribute the remaining size between taskbars
if (num_tasks > 0) {
int task_size = total_size / num_tasks;
if (taskbar_alignment != ALIGN_LEFT)
task_size = MIN(task_size, panel_horizontal ? panel_config.g_task.maximum_width : panel_config.g_task.maximum_height);
for (int i = 0; i < panel->num_desktops; i++) {
Taskbar *taskbar = &panel->taskbar[i];
if (!taskbar->area.on_screen)
@@ -510,6 +543,49 @@ gboolean resize_panel(void *obj)
taskbar->area.height += task_size;
}
}
int slack = total_size - task_size * num_tasks;
if (taskbar_alignment == ALIGN_RIGHT) {
for (int i = 0; i < panel->num_desktops; i++) {
Taskbar *taskbar = &panel->taskbar[i];
if (!taskbar->area.on_screen)
continue;
if (panel_horizontal)
taskbar->area.width += slack;
else
taskbar->area.height += slack;
break;
}
} else if (taskbar_alignment == ALIGN_CENTER) {
slack /= 2;
Taskbar *left_taskbar = NULL;
Taskbar *right_taskbar = NULL;
for (int i = 0; i < panel->num_desktops; i++) {
Taskbar *taskbar = &panel->taskbar[i];
if (!taskbar->area.on_screen)
continue;
if (panel_horizontal)
taskbar->area.width += slack;
else
taskbar->area.height += slack;
taskbar->area.alignment = ALIGN_RIGHT;
left_taskbar = taskbar;
break;
}
for (int i = panel->num_desktops - 1; i >= 0; i--) {
Taskbar *taskbar = &panel->taskbar[i];
if (!taskbar->area.on_screen)
continue;
if (panel_horizontal)
taskbar->area.width += slack;
else
taskbar->area.height += slack;
taskbar->area.alignment = ALIGN_LEFT;
right_taskbar = taskbar;
break;
}
if (left_taskbar == right_taskbar)
left_taskbar->area.alignment = ALIGN_CENTER;
}
} else {
// No tasks => expand the first visible taskbar
for (int i = 0; i < panel->num_desktops; i++) {
@@ -551,7 +627,7 @@ void update_strut(Panel *p)
long struts[12] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
if (panel_horizontal) {
int height = p->area.height + p->marginy;
if (panel_strut_policy == STRUT_MINIMUM || (panel_strut_policy == STRUT_FOLLOW_SIZE && panel_autohide))
if (panel_strut_policy == STRUT_MINIMUM || (panel_strut_policy == STRUT_FOLLOW_SIZE && panel_autohide && p->is_hidden))
height = p->hidden_height;
if (panel_position & TOP) {
struts[2] = height + monitor.y;
@@ -566,7 +642,7 @@ void update_strut(Panel *p)
}
} else {
int width = p->area.width + p->marginx;
if (panel_strut_policy == STRUT_MINIMUM || (panel_strut_policy == STRUT_FOLLOW_SIZE && panel_autohide))
if (panel_strut_policy == STRUT_MINIMUM || (panel_strut_policy == STRUT_FOLLOW_SIZE && panel_autohide && p->is_hidden))
width = p->hidden_width;
if (panel_position & LEFT) {
struts[0] = width + monitor.x;
@@ -814,15 +890,24 @@ void set_panel_properties(Panel *p)
g_free(name);
}
long pid = getpid();
XChangeProperty(server.display,
p->main_win,
server.atom._NET_WM_PID,
XA_CARDINAL,
32,
PropModeReplace,
(unsigned char *)&pid,
1);
// Dock
long val = server.atom._NET_WM_WINDOW_TYPE_DOCK;
XChangeProperty(server.display,
p->main_win,
server.atom._NET_WM_WINDOW_TYPE,
XA_ATOM,
32,
PropModeReplace,
(unsigned char *)&val,
(unsigned char *)&server.atom._NET_WM_WINDOW_TYPE_DOCK,
1);
place_panel_all_desktops(p);
@@ -1009,15 +1094,15 @@ Button *click_button(Panel *panel, int x, int y)
return NULL;
}
void stop_autohide_timeout(Panel *p)
void stop_autohide_timer(Panel *p)
{
stop_timeout(p->autohide_timeout);
stop_timer(&p->autohide_timer);
}
void autohide_show(void *p)
{
Panel *panel = (Panel *)p;
stop_autohide_timeout(panel);
stop_autohide_timer(panel);
panel->is_hidden = 0;
XMapSubwindows(server.display, panel->main_win); // systray windows
set_panel_window_geometry(panel);
@@ -1029,7 +1114,7 @@ void autohide_show(void *p)
void autohide_hide(void *p)
{
Panel *panel = (Panel *)p;
stop_autohide_timeout(panel);
stop_autohide_timer(panel);
set_panel_layer(panel, panel_layer);
panel->is_hidden = TRUE;
XUnmapSubwindows(server.display, panel->main_win); // systray windows
@@ -1041,7 +1126,7 @@ void autohide_trigger_show(Panel *p)
{
if (!p)
return;
change_timeout(&p->autohide_timeout, panel_autohide_show_timeout, 0, autohide_show, p);
change_timer(&p->autohide_timer, true, panel_autohide_show_timeout, 0, autohide_show, p);
}
void autohide_trigger_hide(Panel *p)
@@ -1056,7 +1141,7 @@ void autohide_trigger_hide(Panel *p)
if (child)
return; // mouse over one of the system tray icons
change_timeout(&p->autohide_timeout, panel_autohide_hide_timeout, 0, autohide_hide, p);
change_timer(&p->autohide_timer, true, panel_autohide_hide_timeout, 0, autohide_hide, p);
}
void shrink_panel(Panel *panel)
@@ -1135,6 +1220,63 @@ void _schedule_panel_redraw(const char *file, const char *function, const int li
{
panel_refresh = TRUE;
if (debug_fps) {
fprintf(stderr, YELLOW "%s %s %d: triggering panel redraw" RESET "\n", file, function, line);
fprintf(stderr, YELLOW "tint2: %s %s %d: triggering panel redraw" RESET "\n", file, function, line);
}
}
void save_panel_screenshot(const Panel *panel, const char *path)
{
imlib_context_set_drawable(panel->temp_pmap);
Imlib_Image img = imlib_create_image_from_drawable(0, 0, 0, panel->area.width, panel->area.height, 1);
if (!img) {
XImage *ximg =
XGetImage(server.display, panel->temp_pmap, 0, 0, panel->area.width, panel->area.height, AllPlanes, ZPixmap);
if (ximg) {
DATA32 *pixels = (DATA32 *)calloc(panel->area.width * panel->area.height, sizeof(DATA32));
for (int x = 0; x < panel->area.width; x++) {
for (int y = 0; y < panel->area.height; y++) {
DATA32 xpixel = XGetPixel(ximg, x, y);
DATA32 r = (xpixel >> 16) & 0xff;
DATA32 g = (xpixel >> 8) & 0xff;
DATA32 b = (xpixel >> 0) & 0xff;
DATA32 a = 0x0;
DATA32 argb = (a << 24) | (r << 16) | (g << 8) | b;
pixels[y * panel->area.width + x] = argb;
}
}
XDestroyImage(ximg);
img = imlib_create_image_using_data(panel->area.width, panel->area.height, pixels);
}
}
if (img) {
imlib_context_set_image(img);
if (!panel_horizontal) {
// rotate 90° vertical panel
imlib_image_flip_horizontal();
imlib_image_flip_diagonal();
}
imlib_save_image(path);
imlib_free_image();
}
}
void save_screenshot(const char *path)
{
Panel *panel = &panels[0];
if (panel->area.width > server.monitors[0].width)
panel->area.width = server.monitors[0].width;
panel->temp_pmap =
XCreatePixmap(server.display, server.root_win, panel->area.width, panel->area.height, server.depth);
render_panel(panel);
XSync(server.display, False);
save_panel_screenshot(panel, path);
}

View File

@@ -29,7 +29,6 @@
#include "battery.h"
#endif
extern int signal_pending;
// --------------------------------------------------
// mouse events
extern MouseAction mouse_left;
@@ -93,7 +92,11 @@ extern XSettingsClient *xsettings_client;
extern gboolean startup_notifications;
extern gboolean debug_geometry;
extern gboolean debug_fps;
extern double tracing_fps_threshold;
extern gboolean debug_frames;
extern gboolean debug_thumbnails;
extern double ui_scale_dpi_ref;
extern double ui_scale_monitor_size_ref;
typedef struct Panel {
Area area;
@@ -116,6 +119,7 @@ typedef struct Panel {
int mouse_pressed_alpha;
int mouse_pressed_saturation;
int mouse_pressed_brightness;
double scale;
// Per-panel parameters and states for Taskbar and Task
GlobalTaskbar g_taskbar;
@@ -143,7 +147,7 @@ typedef struct Panel {
gboolean is_hidden;
int hidden_width, hidden_height;
Pixmap hidden_pixmap;
timeout *autohide_timeout;
Timer autohide_timer;
} Panel;
extern Panel panel_config;
@@ -165,7 +169,7 @@ gboolean resize_panel(void *obj);
void render_panel(Panel *panel);
void shrink_panel(Panel *panel);
void _schedule_panel_redraw(const char *file, const char *function, const int line);
#define schedule_panel_redraw() _schedule_panel_redraw(__FILE__, __FUNCTION__, __LINE__)
#define schedule_panel_redraw() _schedule_panel_redraw(__FILE__, __func__, __LINE__)
void set_panel_items_order(Panel *p);
void place_panel_all_desktops(Panel *p);
@@ -207,4 +211,7 @@ void default_font_changed();
void free_icon(Imlib_Image icon);
Imlib_Image scale_icon(Imlib_Image original, int icon_size);
void save_screenshot(const char *path);
void save_panel_screenshot(const Panel *panel, const char *path);
#endif

View File

@@ -115,31 +115,33 @@ void cleanup_separator()
int separator_compute_desired_size(void *obj)
{
Separator *separator = (Separator *)obj;
Panel *panel = (Panel*)separator->area.panel;
if (!separator->area.on_screen)
return 0;
if (panel_horizontal)
return separator->thickness + 2 * separator->area.paddingxlr + left_right_border_width(&separator->area);
return separator->thickness + 2 * separator->area.paddingxlr * panel->scale + left_right_border_width(&separator->area);
else
return separator->thickness + 2 * separator->area.paddingxlr + top_bottom_border_width(&separator->area);
return separator->thickness + 2 * separator->area.paddingxlr * panel->scale + top_bottom_border_width(&separator->area);
}
gboolean resize_separator(void *obj)
{
Separator *separator = (Separator *)obj;
Panel *panel = (Panel*)separator->area.panel;
if (!separator->area.on_screen)
return FALSE;
if (panel_horizontal) {
separator->area.width =
separator->thickness + 2 * separator->area.paddingxlr + left_right_border_width(&separator->area);
separator->thickness + 2 * separator->area.paddingxlr * panel->scale + left_right_border_width(&separator->area);
separator->length =
separator->area.height - 2 * separator->area.paddingy - top_bottom_border_width(&separator->area);
separator->area.height - 2 * separator->area.paddingy * panel->scale - top_bottom_border_width(&separator->area);
} else {
separator->area.height =
separator->thickness + 2 * separator->area.paddingxlr + top_bottom_border_width(&separator->area);
separator->thickness + 2 * separator->area.paddingxlr * panel->scale + top_bottom_border_width(&separator->area);
separator->length =
separator->area.width - 2 * separator->area.paddingy - left_right_border_width(&separator->area);
separator->area.width - 2 * separator->area.paddingy * panel->scale - left_right_border_width(&separator->area);
}
schedule_redraw(&separator->area);

View File

@@ -114,10 +114,10 @@ void init_systray()
return;
systray_composited = !server.disable_transparency && server.visual32 && server.colormap32;
fprintf(stderr, "Systray composited rendering %s\n", systray_composited ? "on" : "off");
fprintf(stderr, "tint2: Systray composited rendering %s\n", systray_composited ? "on" : "off");
if (!systray_composited) {
fprintf(stderr, "systray_asb forced to 100 0 0\n");
fprintf(stderr, "tint2: systray_asb forced to 100 0 0\n");
systray.alpha = 100;
systray.brightness = systray.saturation = 0;
}
@@ -141,11 +141,12 @@ void init_systray_panel(void *p)
void systray_compute_geometry(int *size)
{
Panel *panel = (Panel*)systray.area.panel;
systray.icon_size = panel_horizontal ? systray.area.height : systray.area.width;
systray.icon_size -=
MAX(left_right_border_width(&systray.area), top_bottom_border_width(&systray.area)) + 2 * systray.area.paddingy;
MAX(left_right_border_width(&systray.area), top_bottom_border_width(&systray.area)) + 2 * systray.area.paddingy * panel->scale;
if (systray_max_icon_size > 0)
systray.icon_size = MIN(systray.icon_size, systray_max_icon_size);
systray.icon_size = MIN(systray.icon_size, systray_max_icon_size * panel->scale);
int count = 0;
for (GSList *l = systray.list_icons; l; l = l->next) {
@@ -153,24 +154,24 @@ void systray_compute_geometry(int *size)
}
if (panel_horizontal) {
int height = systray.area.height - top_bottom_border_width(&systray.area) - 2 * systray.area.paddingy;
int height = systray.area.height - top_bottom_border_width(&systray.area) - 2 * systray.area.paddingy * panel->scale;
// here icons_per_column always higher than 0
systray.icons_per_column = (height + systray.area.paddingx) / (systray.icon_size + systray.area.paddingx);
systray.icons_per_column = (height + systray.area.paddingx * panel->scale) / (systray.icon_size + systray.area.paddingx * panel->scale);
systray.margin =
height - (systray.icons_per_column - 1) * (systray.icon_size + systray.area.paddingx) - systray.icon_size;
height - (systray.icons_per_column - 1) * (systray.icon_size + systray.area.paddingx * panel->scale) - systray.icon_size;
systray.icons_per_row = count / systray.icons_per_column + (count % systray.icons_per_column != 0);
*size = left_right_border_width(&systray.area) + 2 * systray.area.paddingxlr +
(systray.icon_size * systray.icons_per_row) + ((systray.icons_per_row - 1) * systray.area.paddingx);
*size = left_right_border_width(&systray.area) + 2 * systray.area.paddingxlr * panel->scale +
(systray.icon_size * systray.icons_per_row) + ((systray.icons_per_row - 1) * systray.area.paddingx * panel->scale);
} else {
int width = systray.area.width - left_right_border_width(&systray.area) - 2 * systray.area.paddingy;
int width = systray.area.width - left_right_border_width(&systray.area) - 2 * systray.area.paddingy * panel->scale;
// here icons_per_row always higher than 0
systray.icons_per_row = (width + systray.area.paddingx) / (systray.icon_size + systray.area.paddingx);
systray.icons_per_row = (width + systray.area.paddingx * panel->scale) / (systray.icon_size + systray.area.paddingx * panel->scale);
systray.margin =
width - (systray.icons_per_row - 1) * (systray.icon_size + systray.area.paddingx) - systray.icon_size;
width - (systray.icons_per_row - 1) * (systray.icon_size + systray.area.paddingx * panel->scale) - systray.icon_size;
systray.icons_per_column = count / systray.icons_per_row + (count % systray.icons_per_row != 0);
*size = top_bottom_border_width(&systray.area) + (2 * systray.area.paddingxlr) +
*size = top_bottom_border_width(&systray.area) + (2 * systray.area.paddingxlr * panel->scale) +
(systray.icon_size * systray.icons_per_column) +
((systray.icons_per_column - 1) * systray.area.paddingx);
((systray.icons_per_column - 1) * systray.area.paddingx * panel->scale);
}
}
@@ -184,7 +185,7 @@ int systray_compute_desired_size(void *obj)
gboolean resize_systray(void *obj)
{
if (systray_profile)
fprintf(stderr, "[%f] %s:%d\n", profiling_get_time(), __FUNCTION__, __LINE__);
fprintf(stderr, "tint2: [%f] %s:%d\n", profiling_get_time(), __func__, __LINE__);
int size;
systray_compute_geometry(&size);
@@ -227,7 +228,7 @@ gboolean resize_systray(void *obj)
void draw_systray(void *obj, cairo_t *c)
{
if (systray_profile)
fprintf(stderr, BLUE "[%f] %s:%d" RESET "\n", profiling_get_time(), __FUNCTION__, __LINE__);
fprintf(stderr, BLUE "tint2: [%f] %s:%d" RESET "\n", profiling_get_time(), __func__, __LINE__);
if (systray_composited) {
if (render_background)
XFreePixmap(server.display, render_background);
@@ -252,12 +253,12 @@ void systray_dump_geometry(void *obj, int indent)
{
Systray *tray = (Systray *)obj;
fprintf(stderr, "%*sIcons:\n", indent, "");
fprintf(stderr, "tint2: %*sIcons:\n", indent, "");
indent += 2;
for (GSList *l = tray->list_icons; l; l = l->next) {
TrayWindow *traywin = (TrayWindow *)l->data;
fprintf(stderr,
"%*sIcon: x = %d, y = %d, w = %d, h = %d, name = %s\n",
"tint2: %*sIcon: x = %d, y = %d, w = %d, h = %d, name = %s\n",
indent,
"",
traywin->x,
@@ -271,7 +272,7 @@ void systray_dump_geometry(void *obj, int indent)
void on_change_systray(void *obj)
{
if (systray_profile)
fprintf(stderr, "[%f] %s:%d\n", profiling_get_time(), __FUNCTION__, __LINE__);
fprintf(stderr, "tint2: [%f] %s:%d\n", profiling_get_time(), __func__, __LINE__);
if (systray.icons_per_column == 0 || systray.icons_per_row == 0)
return;
@@ -281,13 +282,13 @@ void on_change_systray(void *obj)
int posx, posy;
int start;
if (panel_horizontal) {
posy = start = top_border_width(&panel->area) + panel->area.paddingy + top_border_width(&systray.area) +
systray.area.paddingy + systray.margin / 2;
posx = systray.area.posx + left_border_width(&systray.area) + systray.area.paddingxlr;
posy = start = top_border_width(&panel->area) + panel->area.paddingy * panel->scale + top_border_width(&systray.area) +
systray.area.paddingy * panel->scale + systray.margin / 2;
posx = systray.area.posx + left_border_width(&systray.area) + systray.area.paddingxlr * panel->scale;
} else {
posx = start = left_border_width(&panel->area) + panel->area.paddingy + left_border_width(&systray.area) +
systray.area.paddingy + systray.margin / 2;
posy = systray.area.posy + top_border_width(&systray.area) + systray.area.paddingxlr;
posx = start = left_border_width(&panel->area) + panel->area.paddingy * panel->scale + left_border_width(&systray.area) +
systray.area.paddingy * panel->scale + systray.margin / 2;
posy = systray.area.posy + top_border_width(&systray.area) + systray.area.paddingxlr * panel->scale;
}
TrayWindow *traywin;
@@ -301,7 +302,7 @@ void on_change_systray(void *obj)
if (systray_profile)
fprintf(stderr,
"%s:%d win = %lu (%s), parent = %lu, x = %d, y = %d\n",
__FUNCTION__,
__func__,
__LINE__,
traywin->win,
traywin->name,
@@ -312,17 +313,17 @@ void on_change_systray(void *obj)
traywin->height = systray.icon_size;
if (panel_horizontal) {
if (i % systray.icons_per_column) {
posy += systray.icon_size + systray.area.paddingx;
posy += systray.icon_size + systray.area.paddingx * panel->scale;
} else {
posy = start;
posx += (systray.icon_size + systray.area.paddingx);
posx += (systray.icon_size + systray.area.paddingx * panel->scale);
}
} else {
if (i % systray.icons_per_row) {
posx += systray.icon_size + systray.area.paddingx;
posx += systray.icon_size + systray.area.paddingx * panel->scale;
} else {
posx = start;
posy += (systray.icon_size + systray.area.paddingx);
posy += (systray.icon_size + systray.area.paddingx * panel->scale);
}
}
@@ -332,7 +333,7 @@ void on_change_systray(void *obj)
unsigned int width, height, depth;
Window root;
if (!XGetGeometry(server.display, traywin->parent, &root, &xpos, &ypos, &width, &height, &border_width, &depth)) {
fprintf(stderr, RED "Couldn't get geometry of window!" RESET "\n");
fprintf(stderr, RED "tint2: Couldn't get geometry of window!" RESET "\n");
}
if (width != traywin->width || height != traywin->height || xpos != traywin->x || ypos != traywin->y) {
if (systray_profile)
@@ -358,7 +359,7 @@ void on_change_systray(void *obj)
void start_net()
{
if (systray_profile)
fprintf(stderr, "[%f] %s:%d\n", profiling_get_time(), __FUNCTION__, __LINE__);
fprintf(stderr, "tint2: [%f] %s:%d\n", profiling_get_time(), __func__, __LINE__);
if (net_sel_win) {
// protocol already started
if (!systray_enabled)
@@ -395,11 +396,11 @@ void start_net()
&bytes_after,
&prop);
fprintf(stderr, RED "tint2 : another systray is running" RESET);
fprintf(stderr, RED "tint2: another systray is running, cannot use systray" RESET);
if (ret == Success && prop) {
pid = prop[1] * 256;
pid += prop[0];
fprintf(stderr, " pid=%d", pid);
fprintf(stderr, "tint2: pid=%d", pid);
}
fprintf(stderr, RESET "\n");
return;
@@ -407,7 +408,7 @@ void start_net()
// init systray protocol
net_sel_win = XCreateSimpleWindow(server.display, server.root_win, -1, -1, 1, 1, 0, 0, 0);
fprintf(stderr, "systray window %ld\n", net_sel_win);
fprintf(stderr, "tint2: systray window %ld\n", net_sel_win);
// v0.3 trayer specification. tint2 always horizontal.
// Vertical panel will draw the systray horizontal.
@@ -458,13 +459,13 @@ void start_net()
XSetSelectionOwner(server.display, server.atom._NET_SYSTEM_TRAY_SCREEN, net_sel_win, CurrentTime);
if (XGetSelectionOwner(server.display, server.atom._NET_SYSTEM_TRAY_SCREEN) != net_sel_win) {
stop_net();
fprintf(stderr, RED "tint2 : can't get systray manager" RESET "\n");
fprintf(stderr, RED "tint2: cannot find systray manager" RESET "\n");
return;
}
fprintf(stderr, GREEN "tint2 : systray started" RESET "\n");
fprintf(stderr, GREEN "tint2: systray started" RESET "\n");
if (systray_profile)
fprintf(stderr, "[%f] %s:%d\n", profiling_get_time(), __FUNCTION__, __LINE__);
fprintf(stderr, "tint2: [%f] %s:%d\n", profiling_get_time(), __func__, __LINE__);
XClientMessageEvent ev;
ev.type = ClientMessage;
ev.window = server.root_win;
@@ -478,10 +479,10 @@ void start_net()
XSendEvent(server.display, server.root_win, False, StructureNotifyMask, (XEvent *)&ev);
}
void net_message(XClientMessageEvent *e)
void handle_systray_event(XClientMessageEvent *e)
{
if (systray_profile)
fprintf(stderr, "[%f] %s:%d\n", profiling_get_time(), __FUNCTION__, __LINE__);
fprintf(stderr, "tint2: [%f] %s:%d\n", profiling_get_time(), __func__, __LINE__);
Window win;
unsigned long opcode = e->data.l[1];
@@ -499,9 +500,9 @@ void net_message(XClientMessageEvent *e)
default:
if (opcode == server.atom._NET_SYSTEM_TRAY_MESSAGE_DATA)
fprintf(stderr, "message from dockapp: %s\n", e->data.b);
fprintf(stderr, "tint2: message from dockapp: %s\n", e->data.b);
else
fprintf(stderr, RED "SYSTEM_TRAY : unknown message type" RESET "\n");
fprintf(stderr, RED "tint2: SYSTEM_TRAY : unknown message type" RESET "\n");
break;
}
}
@@ -509,7 +510,7 @@ void net_message(XClientMessageEvent *e)
void stop_net()
{
if (systray_profile)
fprintf(stderr, "[%f] %s:%d\n", profiling_get_time(), __FUNCTION__, __LINE__);
fprintf(stderr, "tint2: [%f] %s:%d\n", profiling_get_time(), __func__, __LINE__);
if (systray.list_icons) {
// remove_icon change systray.list_icons
while (systray.list_icons)
@@ -529,10 +530,10 @@ gboolean error;
int window_error_handler(Display *d, XErrorEvent *e)
{
if (systray_profile)
fprintf(stderr, RED "[%f] %s:%d" RESET "\n", profiling_get_time(), __FUNCTION__, __LINE__);
fprintf(stderr, RED "tint2: [%f] %s:%d" RESET "\n", profiling_get_time(), __func__, __LINE__);
error = TRUE;
if (e->error_code != BadWindow) {
fprintf(stderr, RED "systray: error code %d" RESET "\n", e->error_code);
fprintf(stderr, RED "tint2: systray: error code %d" RESET "\n", e->error_code);
}
return 0;
}
@@ -543,11 +544,11 @@ static gint compare_traywindows(gconstpointer a, gconstpointer b)
const TrayWindow *traywin_b = (const TrayWindow *)b;
#if 0
// This breaks pygtk2 StatusIcon with blinking activated
if (traywin_a->empty && !traywin_b->empty)
return 1 * (systray.sort == SYSTRAY_SORT_RIGHT2LEFT ? -1 : 1);
if (!traywin_a->empty && traywin_b->empty)
return -1 * (systray.sort == SYSTRAY_SORT_RIGHT2LEFT ? -1 : 1);
// This breaks pygtk2 StatusIcon with blinking activated
if (traywin_a->empty && !traywin_b->empty)
return 1 * (systray.sort == SYSTRAY_SORT_RIGHT2LEFT ? -1 : 1);
if (!traywin_a->empty && traywin_b->empty)
return -1 * (systray.sort == SYSTRAY_SORT_RIGHT2LEFT ? -1 : 1);
#endif
if (systray.sort == SYSTRAY_SORT_ASCENDING || systray.sort == SYSTRAY_SORT_DESCENDING) {
@@ -564,18 +565,18 @@ static gint compare_traywindows(gconstpointer a, gconstpointer b)
void print_icons()
{
fprintf(stderr, "systray.list_icons: \n");
fprintf(stderr, "tint2: systray.list_icons: \n");
for (GSList *l = systray.list_icons; l; l = l->next) {
TrayWindow *t = l->data;
fprintf(stderr, "%s\n", t->name);
fprintf(stderr, "tint2: %s\n", t->name);
}
fprintf(stderr, "systray.list_icons order: \n");
fprintf(stderr, "tint2: systray.list_icons order: \n");
for (GSList *l = systray.list_icons; l; l = l->next) {
if (l->next) {
TrayWindow *t = l->data;
TrayWindow *u = l->next->data;
int cmp = compare_traywindows(t, u);
fprintf(stderr, "%s %s %s\n", t->name, cmp < 0 ? "<" : cmp == 0 ? "=" : ">", u->name);
fprintf(stderr, "tint2: %s %s %s\n", t->name, cmp < 0 ? "<" : cmp == 0 ? "=" : ">", u->name);
}
}
}
@@ -586,14 +587,14 @@ gboolean reject_icon(Window win)
if (!systray_hide_name_regex) {
systray_hide_name_regex = (regex_t *)calloc(1, sizeof(*systray_hide_name_regex));
if (regcomp(systray_hide_name_regex, systray_hide_name_filter, 0) != 0) {
fprintf(stderr, RED "Could not compile regex %s" RESET "\n", systray_hide_name_filter);
fprintf(stderr, RED "tint2: Could not compile regex %s" RESET "\n", systray_hide_name_filter);
free_and_null(systray_hide_name_regex);
return FALSE;
}
}
char *name = get_window_name(win);
if (regexec(systray_hide_name_regex, name, 0, NULL, 0) == 0) {
fprintf(stderr, GREEN "Filtering out systray icon '%s'" RESET "\n", name);
fprintf(stderr, GREEN "tint2: Filtering out systray icon '%s'" RESET "\n", name);
return TRUE;
}
}
@@ -623,7 +624,7 @@ gboolean add_icon(Window win)
char *name = get_window_name(win);
if (systray_profile)
fprintf(stderr, "[%f] %s:%d win = %lu (%s)\n", profiling_get_time(), __FUNCTION__, __LINE__, win, name);
fprintf(stderr, "tint2: [%f] %s:%d win = %lu (%s)\n", profiling_get_time(), __func__, __LINE__, win, name);
Panel *panel = systray.area.panel;
// Get the process ID of the application that created the window
@@ -655,7 +656,7 @@ gboolean add_icon(Window win)
// Create the parent window that will embed the icon
XWindowAttributes attr;
if (systray_profile)
fprintf(stderr, "XGetWindowAttributes(server.display, win = %ld, &attr)\n", win);
fprintf(stderr, "tint2: XGetWindowAttributes(server.display, win = %ld, &attr)\n", win);
if (XGetWindowAttributes(server.display, win, &attr) == False) {
free(name);
XSelectInput(server.display, win, NoEventMask);
@@ -679,7 +680,7 @@ gboolean add_icon(Window win)
win,
name,
pid,
attr.visual,
(void*)attr.visual,
attr.colormap,
attr.depth,
attr.width,
@@ -706,7 +707,7 @@ gboolean add_icon(Window win)
}
if (systray_profile)
fprintf(stderr, "XCreateWindow(...)\n");
fprintf(stderr, "tint2: XCreateWindow(...)\n");
Window parent = XCreateWindow(server.display,
panel->main_win,
0,
@@ -730,6 +731,8 @@ gboolean add_icon(Window win)
traywin->pid = pid;
traywin->name = name;
traywin->chrono = chrono;
INIT_TIMER(traywin->render_timer);
INIT_TIMER(traywin->resize_timer);
chrono++;
show(&systray.area);
@@ -743,19 +746,19 @@ gboolean add_icon(Window win)
if (!panel->is_hidden) {
if (systray_profile)
fprintf(stderr, "XMapRaised(server.display, traywin->parent)\n");
fprintf(stderr, "tint2: XMapRaised(server.display, traywin->parent)\n");
XMapRaised(server.display, traywin->parent);
}
if (systray_profile)
fprintf(stderr, "[%f] %s:%d\n", profiling_get_time(), __FUNCTION__, __LINE__);
fprintf(stderr, "tint2: [%f] %s:%d\n", profiling_get_time(), __func__, __LINE__);
// Resize and redraw the systray
if (systray_profile)
fprintf(stderr,
BLUE "[%f] %s:%d trigger resize & redraw" RESET "\n",
profiling_get_time(),
__FUNCTION__,
__func__,
__LINE__);
systray.area.resize_needed = TRUE;
panel->area.resize_needed = TRUE;
@@ -770,7 +773,7 @@ gboolean reparent_icon(TrayWindow *traywin)
fprintf(stderr,
"[%f] %s:%d win = %lu (%s)\n",
profiling_get_time(),
__FUNCTION__,
__func__,
__LINE__,
traywin->win,
traywin->name);
@@ -808,7 +811,7 @@ gboolean reparent_icon(TrayWindow *traywin)
e.xclient.data.l[3] = traywin->parent;
e.xclient.data.l[4] = 0;
if (systray_profile)
fprintf(stderr, "XSendEvent(server.display, traywin->win, False, NoEventMask, &e)\n");
fprintf(stderr, "tint2: XSendEvent(server.display, traywin->win, False, NoEventMask, &e)\n");
XSendEvent(server.display, traywin->win, False, NoEventMask, &e);
}
@@ -832,7 +835,7 @@ gboolean reparent_icon(TrayWindow *traywin)
fprintf(stderr,
"[%f] %s:%d win = %lu (%s)\n",
profiling_get_time(),
__FUNCTION__,
__func__,
__LINE__,
traywin->win,
traywin->name);
@@ -846,7 +849,7 @@ gboolean embed_icon(TrayWindow *traywin)
fprintf(stderr,
"[%f] %s:%d win = %lu (%s)\n",
profiling_get_time(),
__FUNCTION__,
__func__,
__LINE__,
traywin->win,
traywin->name);
@@ -859,62 +862,13 @@ gboolean embed_icon(TrayWindow *traywin)
error = FALSE;
XErrorHandler old = XSetErrorHandler(window_error_handler);
if (0) {
Atom acttype;
int actfmt;
unsigned long nbitem, bytes;
unsigned long *data = 0;
int ret;
if (systray_profile)
fprintf(stderr,
"XGetWindowProperty(server.display, traywin->win, server.atom._XEMBED_INFO, 0, 2, False, "
"server.atom._XEMBED_INFO, &acttype, &actfmt, &nbitem, &bytes, &data)\n");
ret = XGetWindowProperty(server.display,
traywin->win,
server.atom._XEMBED_INFO,
0,
2,
False,
server.atom._XEMBED_INFO,
&acttype,
&actfmt,
&nbitem,
&bytes,
(unsigned char **)&data);
if (ret == Success) {
if (data) {
if (nbitem >= 2) {
int hide = ((data[1] & XEMBED_MAPPED) == 0);
if (hide) {
// In theory we have to check the embedding with this and remove icons that refuse embedding.
// In practice we have no idea when the other application processes the event and accepts the
// embed so we cannot check now without a race.
// Race can be triggered with PyGtk(2) apps.
// We could defer this for later (if we set PropertyChangeMask in XSelectInput we get notified)
// but for some reason it breaks transparency for Qt icons. So we don't.
// fprintf(stderr, RED "tint2: window refused embedding" RESET "\n");
// remove_icon(traywin);
// XFree(data);
// return FALSE;
}
}
XFree(data);
}
} else {
fprintf(stderr, RED "tint2 : xembed error" RESET "\n");
remove_icon(traywin);
return FALSE;
}
}
// Redirect rendering when using compositing
if (systray_composited) {
if (systray_profile)
fprintf(stderr, "XDamageCreate(server.display, traywin->parent, XDamageReportRawRectangles)\n");
fprintf(stderr, "tint2: XDamageCreate(server.display, traywin->parent, XDamageReportRawRectangles)\n");
traywin->damage = XDamageCreate(server.display, traywin->parent, XDamageReportRawRectangles);
if (systray_profile)
fprintf(stderr, "XCompositeRedirectWindow(server.display, traywin->parent, CompositeRedirectManual)\n");
fprintf(stderr, "tint2: XCompositeRedirectWindow(server.display, traywin->parent, CompositeRedirectManual)\n");
XCompositeRedirectWindow(server.display, traywin->parent, CompositeRedirectManual);
}
@@ -922,16 +876,16 @@ gboolean embed_icon(TrayWindow *traywin)
// Make the icon visible
if (systray_profile)
fprintf(stderr, "XMapWindow(server.display, traywin->win)\n");
fprintf(stderr, "tint2: XMapWindow(server.display, traywin->win)\n");
XMapWindow(server.display, traywin->win);
if (!panel->is_hidden) {
if (systray_profile)
fprintf(stderr, "XMapRaised(server.display, traywin->parent)\n");
fprintf(stderr, "tint2: XMapRaised(server.display, traywin->parent)\n");
XMapRaised(server.display, traywin->parent);
}
if (systray_profile)
fprintf(stderr, "XSync(server.display, False)\n");
fprintf(stderr, "tint2: XSync(server.display, False)\n");
XSync(server.display, False);
XSetErrorHandler(old);
if (error != FALSE) {
@@ -952,7 +906,7 @@ gboolean embed_icon(TrayWindow *traywin)
fprintf(stderr,
"[%f] %s:%d win = %lu (%s)\n",
profiling_get_time(),
__FUNCTION__,
__func__,
__LINE__,
traywin->win,
traywin->name);
@@ -966,7 +920,7 @@ void remove_icon(TrayWindow *traywin)
fprintf(stderr,
"[%f] %s:%d win = %lu (%s)\n",
profiling_get_time(),
__FUNCTION__,
__func__,
__LINE__,
traywin->win,
traywin->name);
@@ -974,7 +928,7 @@ void remove_icon(TrayWindow *traywin)
// remove from our list
systray.list_icons = g_slist_remove(systray.list_icons, traywin);
fprintf(stderr, YELLOW "remove_icon: %lu (%s)" RESET "\n", traywin->win, traywin->name);
fprintf(stderr, YELLOW "tint2: remove_icon: %lu (%s)" RESET "\n", traywin->win, traywin->name);
XSelectInput(server.display, traywin->win, NoEventMask);
if (traywin->damage)
@@ -989,8 +943,8 @@ void remove_icon(TrayWindow *traywin)
XDestroyWindow(server.display, traywin->parent);
XSync(server.display, False);
XSetErrorHandler(old);
stop_timeout(traywin->render_timeout);
stop_timeout(traywin->resize_timeout);
destroy_timer(&traywin->render_timer);
destroy_timer(&traywin->resize_timer);
free(traywin->name);
if (traywin->image) {
imlib_context_set_image(traywin->image);
@@ -1012,7 +966,7 @@ void remove_icon(TrayWindow *traywin)
fprintf(stderr,
BLUE "[%f] %s:%d trigger resize & redraw" RESET "\n",
profiling_get_time(),
__FUNCTION__,
__func__,
__LINE__);
systray.area.resize_needed = TRUE;
panel->area.resize_needed = TRUE;
@@ -1104,9 +1058,8 @@ void systray_reconfigure_event(TrayWindow *traywin, XEvent *e)
if (traywin->bad_size_counter < min_bad_resize_events) {
systray_resize_icon(traywin);
} else {
if (!traywin->resize_timeout)
traywin->resize_timeout =
add_timeout(fast_resize_period, 0, systray_resize_icon, traywin, &traywin->resize_timeout);
if (!traywin->resize_timer.enabled_)
change_timer(&traywin->resize_timer, true, fast_resize_period, 0, systray_resize_icon, traywin);
}
} else {
if (traywin->bad_size_counter == max_bad_resize_events) {
@@ -1120,14 +1073,13 @@ void systray_reconfigure_event(TrayWindow *traywin, XEvent *e)
// FIXME Normally we should force the icon to resize fill_color to the size we resized it to when we
// embedded it.
// However this triggers a resize loop in new versions of GTK, which we must avoid.
if (!traywin->resize_timeout)
traywin->resize_timeout =
add_timeout(slow_resize_period, 0, systray_resize_icon, traywin, &traywin->resize_timeout);
if (!traywin->resize_timer.enabled_)
change_timer(&traywin->resize_timer, true, slow_resize_period, 0, systray_resize_icon, traywin);
return;
}
} else {
// Correct size
stop_timeout(traywin->resize_timeout);
stop_timer(&traywin->resize_timer);
}
// Resize and redraw the systray
@@ -1135,7 +1087,7 @@ void systray_reconfigure_event(TrayWindow *traywin, XEvent *e)
fprintf(stderr,
BLUE "[%f] %s:%d trigger resize & redraw" RESET "\n",
profiling_get_time(),
__FUNCTION__,
__func__,
__LINE__);
schedule_panel_redraw();
refresh_systray = TRUE;
@@ -1184,9 +1136,8 @@ void systray_resize_request_event(TrayWindow *traywin, XEvent *e)
if (traywin->bad_size_counter < min_bad_resize_events) {
systray_resize_icon(traywin);
} else {
if (!traywin->resize_timeout)
traywin->resize_timeout =
add_timeout(fast_resize_period, 0, systray_resize_icon, traywin, &traywin->resize_timeout);
if (!traywin->resize_timer.enabled_)
change_timer(&traywin->resize_timer, true, fast_resize_period, 0, systray_resize_icon, traywin);
}
} else {
if (traywin->bad_size_counter == max_bad_resize_events) {
@@ -1199,14 +1150,13 @@ void systray_resize_request_event(TrayWindow *traywin, XEvent *e)
// Delayed resize
// FIXME Normally we should force the icon to resize to the size we resized it to when we embedded it.
// However this triggers a resize loop in some versions of GTK, which we must avoid.
if (!traywin->resize_timeout)
traywin->resize_timeout =
add_timeout(slow_resize_period, 0, systray_resize_icon, traywin, &traywin->resize_timeout);
if (!traywin->resize_timer.enabled_)
change_timer(&traywin->resize_timer, true, slow_resize_period, 0, systray_resize_icon, traywin);
return;
}
} else {
// Correct size
stop_timeout(traywin->resize_timeout);
stop_timer(&traywin->resize_timer);
}
// Resize and redraw the systray
@@ -1214,7 +1164,7 @@ void systray_resize_request_event(TrayWindow *traywin, XEvent *e)
fprintf(stderr,
BLUE "[%f] %s:%d trigger resize & redraw" RESET "\n",
profiling_get_time(),
__FUNCTION__,
__func__,
__LINE__);
schedule_panel_redraw();
refresh_systray = TRUE;
@@ -1226,7 +1176,7 @@ void systray_destroy_event(TrayWindow *traywin)
fprintf(stderr,
"[%f] %s:%d win = %lu (%s)\n",
profiling_get_time(),
__FUNCTION__,
__func__,
__LINE__,
traywin->win,
traywin->name);
@@ -1261,7 +1211,7 @@ void systray_render_icon_composited(void *t)
fprintf(stderr,
"[%f] %s:%d win = %lu (%s)\n",
profiling_get_time(),
__FUNCTION__,
__func__,
__LINE__,
traywin->win,
traywin->name);
@@ -1273,13 +1223,12 @@ void systray_render_icon_composited(void *t)
if (compare_timespecs(&earliest_render, &now) > 0) {
traywin->num_fast_renders++;
if (traywin->num_fast_renders > max_fast_refreshes) {
traywin->render_timeout =
add_timeout(min_refresh_period, 0, systray_render_icon_composited, traywin, &traywin->render_timeout);
change_timer(&traywin->render_timer, true, min_refresh_period, 0, systray_render_icon_composited, traywin);
if (systray_profile)
fprintf(stderr,
YELLOW "[%f] %s:%d win = %lu (%s) delaying rendering" RESET "\n",
profiling_get_time(),
__FUNCTION__,
__func__,
__LINE__,
traywin->win,
traywin->name);
@@ -1293,23 +1242,19 @@ void systray_render_icon_composited(void *t)
if (traywin->width == 0 || traywin->height == 0) {
// reschedule rendering since the geometry information has not yet been processed (can happen on slow cpu)
traywin->render_timeout =
add_timeout(min_refresh_period, 0, systray_render_icon_composited, traywin, &traywin->render_timeout);
change_timer(&traywin->render_timer, true, min_refresh_period, 0, systray_render_icon_composited, traywin);
if (systray_profile)
fprintf(stderr,
YELLOW "[%f] %s:%d win = %lu (%s) delaying rendering" RESET "\n",
profiling_get_time(),
__FUNCTION__,
__func__,
__LINE__,
traywin->win,
traywin->name);
return;
}
if (traywin->render_timeout) {
stop_timeout(traywin->render_timeout);
traywin->render_timeout = NULL;
}
stop_timer(&traywin->render_timer);
// good systray icons support 32 bit depth, but some icons are still 24 bit.
// We create a heuristic mask for these icons, i.e. we get the rgb value in the top left corner, and
@@ -1328,7 +1273,7 @@ void systray_render_icon_composited(void *t)
} else if (traywin->depth == 32) {
f = XRenderFindStandardFormat(server.display, PictStandardARGB32);
} else {
fprintf(stderr, RED "Strange tray icon found with depth: %d" RESET "\n", traywin->depth);
fprintf(stderr, RED "tint2: Strange tray icon found with depth: %d" RESET "\n", traywin->depth);
XFreePixmap(server.display, tmp_pmap);
return;
}
@@ -1430,7 +1375,7 @@ void systray_render_icon_composited(void *t)
fprintf(stderr,
"[%f] %s:%d win = %lu (%s)\n",
profiling_get_time(),
__FUNCTION__,
__func__,
__LINE__,
traywin->win,
traywin->name);
@@ -1468,13 +1413,11 @@ void systray_render_icon(void *t)
// fprintf(stderr,
// YELLOW "[%f] %s:%d win = %lu (%s) delaying rendering" RESET "\n",
// profiling_get_time(),
// __FUNCTION__,
// __func__,
// __LINE__,
// traywin->win,
// traywin->name);
stop_timeout(traywin->render_timeout);
traywin->render_timeout =
add_timeout(min_refresh_period, 0, systray_render_icon, traywin, &traywin->render_timeout);
change_timer(&traywin->render_timer, true, min_refresh_period, 0, systray_render_icon, traywin);
return;
}
@@ -1482,7 +1425,7 @@ void systray_render_icon(void *t)
fprintf(stderr,
"[%f] %s:%d win = %lu (%s)\n",
profiling_get_time(),
__FUNCTION__,
__func__,
__LINE__,
traywin->win,
traywin->name);
@@ -1497,25 +1440,19 @@ void systray_render_icon(void *t)
unsigned int width, height, depth;
Window root;
if (!XGetGeometry(server.display, traywin->win, &root, &xpos, &ypos, &width, &height, &border_width, &depth)) {
stop_timeout(traywin->render_timeout);
if (!traywin->resize_timeout)
traywin->render_timeout =
add_timeout(min_refresh_period, 0, systray_render_icon, traywin, &traywin->render_timeout);
change_timer(&traywin->render_timer, true, min_refresh_period, 0, systray_render_icon, traywin);
systray_render_icon_from_image(traywin);
XSetErrorHandler(old);
return;
} else {
if (xpos != 0 || ypos != 0 || width != traywin->width || height != traywin->height) {
stop_timeout(traywin->render_timeout);
if (!traywin->resize_timeout)
traywin->render_timeout =
add_timeout(min_refresh_period, 0, systray_render_icon, traywin, &traywin->render_timeout);
change_timer(&traywin->render_timer, true, min_refresh_period, 0, systray_render_icon, traywin);
systray_render_icon_from_image(traywin);
if (systray_profile)
fprintf(stderr,
YELLOW "[%f] %s:%d win = %lu (%s) delaying rendering" RESET "\n",
profiling_get_time(),
__FUNCTION__,
__func__,
__LINE__,
traywin->win,
traywin->name);
@@ -1527,7 +1464,7 @@ void systray_render_icon(void *t)
}
if (systray_profile)
fprintf(stderr, "rendering tray icon\n");
fprintf(stderr, "tint2: rendering tray icon\n");
if (systray_composited) {
systray_render_icon_composited(traywin);
@@ -1549,7 +1486,7 @@ void systray_render_icon(void *t)
void refresh_systray_icons()
{
if (systray_profile)
fprintf(stderr, BLUE "[%f] %s:%d" RESET "\n", profiling_get_time(), __FUNCTION__, __LINE__);
fprintf(stderr, BLUE "tint2: [%f] %s:%d" RESET "\n", profiling_get_time(), __func__, __LINE__);
TrayWindow *traywin;
GSList *l;
for (l = systray.list_icons; l; l = l->next) {

View File

@@ -56,11 +56,11 @@ typedef struct {
// Members used for rendering
struct timespec time_last_render;
int num_fast_renders;
timeout *render_timeout;
Timer render_timer;
// Members used for resizing
int bad_size_counter;
struct timespec time_last_resize;
timeout *resize_timeout;
Timer resize_timer;
// Icon contents if we are compositing the icon, otherwise null
Imlib_Image image;
// XDamage
@@ -96,7 +96,7 @@ gboolean systray_on_monitor(int i_monitor, int num_panels);
// many tray icon doesn't manage stop/restart of the systray manager
void start_net();
void stop_net();
void net_message(XClientMessageEvent *e);
void handle_systray_event(XClientMessageEvent *e);
gboolean add_icon(Window id);
gboolean reparent_icon(TrayWindow *traywin);

View File

@@ -35,11 +35,13 @@
#include "tooltip.h"
#include "window.h"
timeout *urgent_timeout;
Timer urgent_timer;
GSList *urgent_list;
void task_dump_geometry(void *obj, int indent);
int task_compute_desired_size(void *obj);
void task_refresh_thumbnail(Task *task);
void task_get_content_color(void *obj, Color *color);
char *task_get_tooltip(void *obj)
{
@@ -47,6 +49,17 @@ char *task_get_tooltip(void *obj)
return strdup(t->title);
}
cairo_surface_t *task_get_thumbnail(void *obj)
{
if (!panel_config.g_task.thumbnail_enabled)
return NULL;
Task *t = (Task *)obj;
if (!t->thumbnail)
task_refresh_thumbnail(t);
taskbar_start_thumbnail_timer(THUMB_MODE_TOOLTIP_WINDOW);
return t->thumbnail;
}
Task *add_task(Window win)
{
if (!win)
@@ -73,6 +86,7 @@ Task *add_task(Window win)
task_template.area.has_mouse_press_effect = panel_config.mouse_effects;
task_template.area._dump_geometry = task_dump_geometry;
task_template.area._is_under_mouse = full_width_area_is_under_mouse;
task_template.area._get_content_color = task_get_content_color;
task_template.win = win;
task_template.desktop = get_window_desktop(win);
task_template.area.panel = &panels[monitor];
@@ -93,9 +107,20 @@ Task *add_task(Window win)
(int)win,
task_template.title ? task_template.title : "null");
// fprintf(stderr, "%s %d: win = %ld, task = %s\n", __FUNCTION__, __LINE__, win, task_template.title ?
// task_template.title : "??");
// fprintf(stderr, "new task %s win %u: desktop %d, monitor %d\n", new_task.title, win, new_task.desktop, monitor);
// get application name
// use res_class property of WM_CLASS as res_name is easily overridable by user
XClassHint *classhint = XAllocClassHint();
if (classhint && XGetClassHint(server.display, win, classhint))
task_template.application = strdup(classhint->res_class);
else
task_template.application = strdup("Untitled");
if (classhint) {
if (classhint->res_name)
XFree(classhint->res_name);
if (classhint->res_class)
XFree(classhint->res_class);
XFree(classhint);
}
GPtrArray *task_buttons = g_ptr_array_new();
for (int j = 0; j < panels[monitor].num_desktops; j++) {
@@ -110,6 +135,7 @@ Task *add_task(Window win)
task_instance->area._dump_geometry = task_dump_geometry;
task_instance->area._is_under_mouse = full_width_area_is_under_mouse;
task_instance->area._compute_desired_size = task_compute_desired_size;
task_instance->area._get_content_color = task_get_content_color;
task_instance->win = task_template.win;
task_instance->desktop = task_template.desktop;
task_instance->win_x = task_template.win_x;
@@ -118,12 +144,17 @@ Task *add_task(Window win)
task_instance->win_h = task_template.win_h;
task_instance->current_state = TASK_UNDEFINED; // to update the current state later in set_task_state...
if (task_instance->desktop == ALL_DESKTOPS && server.desktop != j) {
// fprintf(stderr, "%s %d: win = %ld hiding task: another desktop\n", __FUNCTION__, __LINE__, win);
task_instance->area.on_screen = always_show_all_desktop_tasks;
}
task_instance->title = task_template.title;
if (panels[monitor].g_task.tooltip_enabled)
task_instance->application = task_template.application;
if (panels[monitor].g_task.tooltip_enabled) {
task_instance->area._get_tooltip_text = task_get_tooltip;
task_instance->area._get_tooltip_image = task_get_thumbnail;
}
task_instance->icon_color = task_template.icon_color;
task_instance->icon_color_hover = task_template.icon_color_hover;
task_instance->icon_color_press = task_template.icon_color_press;
for (int k = 0; k < TASK_STATE_COUNT; ++k) {
task_instance->icon[k] = task_template.icon[k];
task_instance->icon_hover[k] = task_template.icon_hover[k];
@@ -158,26 +189,10 @@ Task *add_task(Window win)
return (Task *)g_ptr_array_index(task_buttons, 0);
}
void remove_task(Task *task)
void task_remove_icon(Task *task)
{
if (!task)
return;
// fprintf(stderr, "%s %d: win = %ld, task = %s\n", __FUNCTION__, __LINE__, task->win, task->title ? task->title :
// "??");
if (taskbar_mode == MULTI_DESKTOP) {
Panel *panel = task->area.panel;
panel->area.resize_needed = 1;
}
Window win = task->win;
// free title and icon just for the first task
// even with task_on_all_desktop and with task_on_all_panel
// printf("remove_task %s %d\n", task->title, task->desktop);
if (task->title)
free(task->title);
for (int k = 0; k < TASK_STATE_COUNT; ++k) {
if (task->icon[k]) {
imlib_context_set_image(task->icon[k]);
@@ -195,6 +210,29 @@ void remove_task(Task *task)
task->icon_press[k] = 0;
}
}
}
void remove_task(Task *task)
{
if (!task)
return;
if (taskbar_mode == MULTI_DESKTOP) {
Panel *panel = task->area.panel;
panel->area.resize_needed = 1;
}
Window win = task->win;
// free title, icon and application name just for the first task
// even with task_on_all_desktop and with task_on_all_panel
if (task->title)
free(task->title);
if (task->thumbnail)
cairo_surface_destroy(task->thumbnail);
if (task->application)
free(task->application);
task_remove_icon(task);
GPtrArray *task_buttons = g_hash_table_lookup(win_to_task, &win);
for (int i = 0; i < task_buttons->len; ++i) {
@@ -205,6 +243,8 @@ void remove_task(Task *task)
task_drag = 0;
if (g_slist_find(urgent_list, task2))
del_urgent(task2);
if (g_tooltip.area == &task2->area)
tooltip_hide(NULL);
remove_area((Area *)task2);
free(task2);
}
@@ -259,48 +299,31 @@ gboolean task_update_title(Task *task)
return TRUE;
}
void task_update_icon(Task *task)
Imlib_Image task_get_icon(Window win, int icon_size)
{
Panel *panel = task->area.panel;
if (!panel->g_task.has_icon)
return;
for (int k = 0; k < TASK_STATE_COUNT; ++k) {
if (task->icon[k]) {
imlib_context_set_image(task->icon[k]);
imlib_free_image();
task->icon[k] = 0;
}
}
Imlib_Image img = NULL;
if (!img) {
int i;
gulong *data = server_get_property(task->win, server.atom._NET_WM_ICON, XA_CARDINAL, &i);
int len;
gulong *data = server_get_property(win, server.atom._NET_WM_ICON, XA_CARDINAL, &len);
if (data) {
// get ARGB icon
int w, h;
gulong *tmp_data;
tmp_data = get_best_icon(data, get_icon_count(data, i), i, &w, &h, panel->g_task.icon_size1);
DATA32 icon_data[w * h];
for (int j = 0; j < w * h; ++j)
icon_data[j] = tmp_data[j];
img = imlib_create_image_using_copied_data(w, h, icon_data);
if (0 && img)
fprintf(stderr,
"%s: Got %dx%d icon via _NET_WM_ICON for %s\n",
__FUNCTION__,
w,
h,
task->title ? task->title : "task");
if (len > 0) {
// get ARGB icon
int w, h;
gulong *tmp_data = get_best_icon(data, get_icon_count(data, len), len, &w, &h, icon_size);
if (tmp_data) {
DATA32 icon_data[w * h];
for (int j = 0; j < w * h; ++j)
icon_data[j] = tmp_data[j];
img = imlib_create_image_using_copied_data(w, h, icon_data);
}
}
XFree(data);
}
}
if (!img) {
XWMHints *hints = XGetWMHints(server.display, task->win);
XWMHints *hints = XGetWMHints(server.display, win);
if (hints) {
if (hints->flags & IconPixmapHint && hints->icon_pixmap != 0) {
// get width, height and depth for the pixmap
@@ -312,13 +335,6 @@ void task_update_icon(Task *task)
XGetGeometry(server.display, hints->icon_pixmap, &root, &icon_x, &icon_y, &w, &h, &border_width, &bpp);
imlib_context_set_drawable(hints->icon_pixmap);
img = imlib_create_image_from_drawable(hints->icon_mask, 0, 0, w, h, 0);
if (0 && img)
fprintf(stderr,
"%s: Got %dx%d pixmap icon via WM_HINTS for %s\n",
__FUNCTION__,
w,
h,
task->title ? task->title : "task");
}
XFree(hints);
}
@@ -327,10 +343,46 @@ void task_update_icon(Task *task)
if (img == NULL) {
imlib_context_set_image(default_icon);
img = imlib_clone_image();
if (0)
fprintf(stderr, "%s: Using default icon for %s\n", __FUNCTION__, task->title ? task->title : "task");
}
return img;
}
void task_set_icon_color(Task *task, Imlib_Image icon)
{
get_image_mean_color(icon, &task->icon_color);
if (panel_config.mouse_effects) {
task->icon_color_hover = task->icon_color;
adjust_color(&task->icon_color_hover,
panel_config.mouse_over_alpha,
panel_config.mouse_over_saturation,
panel_config.mouse_over_brightness);
task->icon_color_press = task->icon_color;
adjust_color(&task->icon_color_press,
panel_config.mouse_pressed_alpha,
panel_config.mouse_pressed_saturation,
panel_config.mouse_pressed_brightness);
}
}
void task_update_icon(Task *task)
{
Panel *panel = task->area.panel;
if (!panel->g_task.has_icon) {
if (panel_config.g_task.has_content_tint) {
Imlib_Image img = task_get_icon(task->win, panel->g_task.icon_size1);
task_set_icon_color(task, img);
imlib_context_set_image(img);
imlib_free_image();
}
return;
}
task_remove_icon(task);
Imlib_Image img = task_get_icon(task->win, panel->g_task.icon_size1);
task_set_icon_color(task, img);
// transform icons
imlib_context_set_image(img);
imlib_image_set_has_alpha(1);
@@ -344,20 +396,10 @@ void task_update_icon(Task *task)
task->icon_width = imlib_image_get_width();
task->icon_height = imlib_image_get_height();
for (int k = 0; k < TASK_STATE_COUNT; ++k) {
imlib_context_set_image(orig_image);
task->icon[k] = imlib_clone_image();
imlib_context_set_image(task->icon[k]);
DATA32 *data32;
if (panel->g_task.alpha[k] != 100 || panel->g_task.saturation[k] != 0 || panel->g_task.brightness[k] != 0) {
data32 = imlib_image_get_data();
adjust_asb(data32,
task->icon_width,
task->icon_height,
panel->g_task.alpha[k] / 100.0,
panel->g_task.saturation[k] / 100.0,
panel->g_task.brightness[k] / 100.0);
imlib_image_put_back_data(data32);
}
task->icon[k] = adjust_icon(orig_image,
panel->g_task.alpha[k],
panel->g_task.saturation[k],
panel->g_task.brightness[k]);
if (panel_config.mouse_effects) {
task->icon_hover[k] = adjust_icon(task->icon[k],
panel_config.mouse_over_alpha,
@@ -375,9 +417,12 @@ void task_update_icon(Task *task)
GPtrArray *task_buttons = get_task_buttons(task->win);
if (task_buttons) {
for (int i = 0; i < task_buttons->len; ++i) {
Task *task2 = g_ptr_array_index(task_buttons, i);
Task *task2 = (Task *)g_ptr_array_index(task_buttons, i);
task2->icon_width = task->icon_width;
task2->icon_height = task->icon_height;
task2->icon_color = task->icon_color;
task2->icon_color_hover = task->icon_color_hover;
task2->icon_color_press = task->icon_color_press;
for (int k = 0; k < TASK_STATE_COUNT; ++k) {
task2->icon[k] = task->icon[k];
task2->icon_hover[k] = task->icon_hover[k];
@@ -402,7 +447,7 @@ void draw_task_icon(Task *task, int text_width)
else
task->_icon_x = (task->area.width - panel->g_task.icon_size1) / 2;
} else {
task->_icon_x = left_border_width(&task->area) + task->area.paddingxlr;
task->_icon_x = left_border_width(&task->area) + task->area.paddingxlr * panel->scale;
}
// Render
@@ -432,11 +477,13 @@ void draw_task(void *obj, cairo_t *c)
task->_text_width = 0;
if (panel->g_task.has_text) {
PangoLayout *layout = pango_cairo_create_layout(c);
PangoContext *context = pango_cairo_create_context(c);
pango_cairo_context_set_resolution(context, 96 * panel->scale);
PangoLayout *layout = pango_layout_new(context);
pango_layout_set_font_description(layout, panel->g_task.font_desc);
pango_layout_set_text(layout, task->title, -1);
pango_layout_set_width(layout, ((Taskbar *)task->area.parent)->text_width * PANGO_SCALE);
pango_layout_set_width(layout, (((Taskbar *)task->area.parent)->text_width + TINT2_PANGO_SLACK) * PANGO_SCALE);
pango_layout_set_height(layout, panel->g_task.text_height * PANGO_SCALE);
pango_layout_set_wrap(layout, PANGO_WRAP_WORD_CHAR);
pango_layout_set_ellipsize(layout, PANGO_ELLIPSIZE_END);
@@ -453,6 +500,7 @@ void draw_task(void *obj, cairo_t *c)
draw_text(layout, c, panel->g_task.text_posx, task->_text_posy, config_text, panel->font_shadow);
g_object_unref(layout);
g_object_unref(context);
}
if (panel->g_task.has_icon)
@@ -465,7 +513,7 @@ void task_dump_geometry(void *obj, int indent)
Panel *panel = (Panel *)task->area.panel;
fprintf(stderr,
"%*sText: x = %d, y = %d, w = %d, h = %d, align = %s, text = %s\n",
"tint2: %*sText: x = %d, y = %d, w = %d, h = %d, align = %s, text = %s\n",
indent,
"",
(int)panel->g_task.text_posx,
@@ -475,7 +523,7 @@ void task_dump_geometry(void *obj, int indent)
panel->g_task.centered ? "center" : "left",
task->title);
fprintf(stderr,
"%*sIcon: x = %d, y = %d, w = h = %d\n",
"tint2: %*sIcon: x = %d, y = %d, w = h = %d\n",
indent,
"",
task->_icon_x,
@@ -483,11 +531,29 @@ void task_dump_geometry(void *obj, int indent)
panel->g_task.icon_size1);
}
void task_get_content_color(void *obj, Color *color)
{
Task *task = (Task *)obj;
Color *content_color = NULL;
if (panel_config.mouse_effects) {
if (task->area.mouse_state == MOUSE_OVER)
content_color = &task->icon_color_hover;
else if (task->area.mouse_state == MOUSE_DOWN)
content_color = &task->icon_color_press;
else
content_color = &task->icon_color;
} else {
content_color = &task->icon_color;
}
if (content_color)
*color = *content_color;
}
int task_compute_desired_size(void *obj)
{
Task *task = (Task *)obj;
Panel *panel = (Panel *)task->area.panel;
int size = panel_horizontal ? panel->g_task.maximum_width : panel->g_task.maximum_height;
int size = (panel_horizontal ? panel->g_task.maximum_width : panel->g_task.maximum_height) * panel->scale;
return size;
}
@@ -590,7 +656,6 @@ void reset_active_task()
}
Window w1 = get_active_window();
// printf("Change active task %ld\n", w1);
if (w1) {
if (!get_task_buttons(w1)) {
@@ -602,11 +667,50 @@ void reset_active_task()
}
}
void task_refresh_thumbnail(Task *task)
{
if (!panel_config.g_task.thumbnail_enabled)
return;
if (task->current_state == TASK_ICONIFIED)
return;
Panel *panel = (Panel*)task->area.panel;
double now = get_time();
if (now - task->thumbnail_last_update < 0.1)
return;
if (debug_thumbnails)
fprintf(stderr, "tint2: thumbnail for window: %s" RESET "\n", task->title ? task->title : "");
cairo_surface_t *thumbnail = get_window_thumbnail(task->win, panel_config.g_task.thumbnail_width * panel->scale);
if (!thumbnail)
return;
if (task->thumbnail)
cairo_surface_destroy(task->thumbnail);
task->thumbnail = thumbnail;
task->thumbnail_last_update = get_time();
if (debug_thumbnails)
fprintf(stderr,
YELLOW "tint2: %s took %f ms (window: %s)" RESET "\n",
__func__,
1000 * (task->thumbnail_last_update - now),
task->title ? task->title : "");
if (g_tooltip.mapped && (g_tooltip.area == &task->area)) {
tooltip_update_contents_for(&task->area);
tooltip_update();
}
}
void set_task_state(Task *task, TaskState state)
{
if (!task || state == TASK_UNDEFINED || state >= TASK_STATE_COUNT)
return;
if (!task->thumbnail)
task_refresh_thumbnail(task);
if (state == TASK_ACTIVE) {
// For active windows, we get the thumbnail twice with a small delay in between.
// This is because they sometimes redraw their windows slowly.
taskbar_start_thumbnail_timer(THUMB_MODE_ACTIVE_WINDOW);
}
if (state == TASK_ACTIVE && task->current_state != state) {
clock_gettime(CLOCK_MONOTONIC, &task->last_activation_time);
if (taskbar_sort_method == TASKBAR_SORT_LRU || taskbar_sort_method == TASKBAR_SORT_MRU) {
@@ -621,7 +725,7 @@ void set_task_state(Task *task, TaskState state)
}
}
if (task->current_state != state || hide_task_diff_monitor) {
if (task->current_state != state || hide_task_diff_monitor || hide_task_diff_desktop) {
GPtrArray *task_buttons = get_task_buttons(task->win);
if (task_buttons) {
for (int i = 0; i < task_buttons->len; ++i) {
@@ -645,6 +749,10 @@ void set_task_state(Task *task, TaskState state)
hide = TRUE;
}
}
if (hide_task_diff_desktop) {
if (taskbar->desktop != server.desktop)
hide = TRUE;
}
if (get_window_monitor(task->win) != ((Panel *)task->area.panel)->monitor &&
(hide_task_diff_monitor || num_panels > 1)) {
hide = TRUE;
@@ -668,8 +776,8 @@ void blink_urgent(void *arg)
GSList *urgent_task = urgent_list;
while (urgent_task) {
Task *t = urgent_task->data;
if (t->urgent_tick < max_tick_urgent) {
if (t->urgent_tick++ % 2)
if (t->urgent_tick <= max_tick_urgent) {
if (++t->urgent_tick % 2)
set_task_state(t, TASK_URGENT);
else
set_task_state(t, window_is_iconified(t->win) ? TASK_ICONIFIED : TASK_NORMAL);
@@ -696,8 +804,8 @@ void add_urgent(Task *task)
// not yet in the list, so we have to add it
urgent_list = g_slist_prepend(urgent_list, task);
if (!urgent_timeout)
urgent_timeout = add_timeout(10, 1000, blink_urgent, 0, &urgent_timeout);
if (!urgent_timer.enabled_)
change_timer(&urgent_timer, true, 10, 1000, blink_urgent, 0);
Panel *panel = task->area.panel;
if (panel->is_hidden)
@@ -707,8 +815,78 @@ void add_urgent(Task *task)
void del_urgent(Task *task)
{
urgent_list = g_slist_remove(urgent_list, task);
if (!urgent_list) {
stop_timeout(urgent_timeout);
urgent_timeout = NULL;
if (!urgent_list)
stop_timer(&urgent_timer);
}
void task_handle_mouse_event(Task *task, MouseAction action)
{
if (!task)
return;
switch (action) {
case NONE:
break;
case CLOSE:
close_window(task->win);
break;
case TOGGLE:
activate_window(task->win);
break;
case ICONIFY:
XIconifyWindow(server.display, task->win, server.screen);
break;
case TOGGLE_ICONIFY:
if (active_task && task->win == active_task->win)
XIconifyWindow(server.display, task->win, server.screen);
else
activate_window(task->win);
break;
case SHADE:
toggle_window_shade(task->win);
break;
case MAXIMIZE_RESTORE:
toggle_window_maximized(task->win);
break;
case MAXIMIZE:
toggle_window_maximized(task->win);
break;
case RESTORE:
toggle_window_maximized(task->win);
break;
case DESKTOP_LEFT: {
if (task->desktop == 0)
break;
int desktop = task->desktop - 1;
change_window_desktop(task->win, desktop);
if (desktop == server.desktop)
activate_window(task->win);
break;
}
case DESKTOP_RIGHT: {
if (task->desktop == server.num_desktops)
break;
int desktop = task->desktop + 1;
change_window_desktop(task->win, desktop);
if (desktop == server.desktop)
activate_window(task->win);
break;
}
case NEXT_TASK: {
Task *task1 = next_task(find_active_task(task));
activate_window(task1->win);
} break;
case PREV_TASK: {
Task *task1 = prev_task(find_active_task(task));
activate_window(task1->win);
}
}
}
void task_update_desktop(Task *task)
{
Window win = task->win;
remove_task(task);
task = add_task(win);
reset_active_task();
schedule_panel_redraw();
}

View File

@@ -10,6 +10,7 @@
#include <X11/Xlib.h>
#include <pango/pangocairo.h>
#include <Imlib2.h>
#include "common.h"
#include "timer.h"
@@ -41,10 +42,13 @@ typedef struct GlobalTask {
// starting position for text ~ task_padding + task_border + icon_size
double text_posx, text_height;
gboolean has_font;
gboolean has_content_tint;
PangoFontDescription *font_desc;
Color font[TASK_STATE_COUNT];
int config_font_mask;
gboolean tooltip_enabled;
gboolean thumbnail_enabled;
int thumbnail_width;
} GlobalTask;
// Stores information about a task.
@@ -60,7 +64,11 @@ typedef struct Task {
Imlib_Image icon_press[TASK_STATE_COUNT];
unsigned int icon_width;
unsigned int icon_height;
Color icon_color;
Color icon_color_hover;
Color icon_color_press;
char *title;
char *application;
int urgent_tick;
// These may not be up-to-date
int win_x;
@@ -73,9 +81,11 @@ typedef struct Task {
double _text_posy;
int _icon_x;
int _icon_y;
cairo_surface_t *thumbnail;
double thumbnail_last_update;
} Task;
extern timeout *urgent_timeout;
extern Timer urgent_timer;
extern GSList *urgent_list;
Task *add_task(Window win);
@@ -85,9 +95,12 @@ void draw_task(void *obj, cairo_t *c);
void on_change_task(void *obj);
void task_update_icon(Task *task);
void task_update_desktop(Task *task);
gboolean task_update_title(Task *task);
void reset_active_task();
void set_task_state(Task *task, TaskState state);
void task_handle_mouse_event(Task *task, MouseAction action);
void task_refresh_thumbnail(Task *task);
// Given a pointer to the task that is currently under the mouse (current_task),
// returns a pointer to the Task for the active window on the same taskbar.

View File

@@ -32,6 +32,7 @@
#include "window.h"
#include "panel.h"
#include "strnatcmp.h"
#include "tooltip.h"
GHashTable *win_to_task;
@@ -39,12 +40,19 @@ Task *active_task;
Task *task_drag;
gboolean taskbar_enabled;
gboolean taskbar_distribute_size;
gboolean hide_task_diff_desktop;
gboolean hide_inactive_tasks;
gboolean hide_task_diff_monitor;
gboolean hide_taskbar_if_empty;
gboolean always_show_all_desktop_tasks;
TaskbarSortMethod taskbar_sort_method;
Alignment taskbar_alignment;
static Timer thumbnail_update_timer_all;
static Timer thumbnail_update_timer_active;
static Timer thumbnail_update_timer_tooltip;
static GList *taskbar_task_orderings = NULL;
static GList *taskbar_thumbnail_jobs_done = NULL;
void taskbar_init_fonts();
int taskbar_compute_desired_size(void *obj);
@@ -52,6 +60,8 @@ int taskbar_compute_desired_size(void *obj);
// Removes the task with &win = key. The other args are ignored.
void taskbar_remove_task(Window *win);
void taskbar_update_thumbnails(void *arg);
guint win_hash(gconstpointer key)
{
return *((const Window *)key);
@@ -70,21 +80,61 @@ void free_ptr_array(gpointer data)
void default_taskbar()
{
win_to_task = NULL;
urgent_timeout = NULL;
urgent_list = NULL;
taskbar_enabled = FALSE;
taskbar_distribute_size = FALSE;
hide_task_diff_desktop = FALSE;
hide_inactive_tasks = FALSE;
hide_task_diff_monitor = FALSE;
hide_taskbar_if_empty = FALSE;
always_show_all_desktop_tasks = FALSE;
taskbar_thumbnail_jobs_done = NULL;
taskbar_sort_method = TASKBAR_NOSORT;
taskbar_alignment = ALIGN_LEFT;
default_taskbarname();
}
void taskbar_clear_orderings()
{
if (!taskbar_task_orderings)
return;
for (GList *order = taskbar_task_orderings; order; order = order->next) {
g_list_free_full((GList *)order->data, free);
}
g_list_free(taskbar_task_orderings);
taskbar_task_orderings = NULL;
}
void taskbar_save_orderings()
{
taskbar_clear_orderings();
taskbar_task_orderings = NULL;
for (int i = 0; i < num_panels; i++) {
Panel *panel = &panels[i];
for (int j = 0; j < panel->num_desktops; j++) {
Taskbar *taskbar = &panel->taskbar[j];
GList *task_order = NULL;
for (GList *c = (taskbar->area.children && taskbarname_enabled) ? taskbar->area.children->next
: taskbar->area.children;
c;
c = c->next) {
Task *t = (Task *)c->data;
Window *window = calloc(1, sizeof(Window));
*window = t->win;
task_order = g_list_append(task_order, window);
}
taskbar_task_orderings = g_list_append(taskbar_task_orderings, task_order);
}
}
}
void cleanup_taskbar()
{
destroy_timer(&thumbnail_update_timer_all);
destroy_timer(&thumbnail_update_timer_active);
destroy_timer(&thumbnail_update_timer_tooltip);
g_list_free(taskbar_thumbnail_jobs_done);
taskbar_save_orderings();
if (win_to_task) {
while (g_hash_table_size(win_to_task)) {
GHashTableIter iter;
@@ -116,7 +166,7 @@ void cleanup_taskbar()
g_slist_free(urgent_list);
urgent_list = NULL;
stop_timeout(urgent_timeout);
destroy_timer(&urgent_timer);
for (int state = 0; state < TASK_STATE_COUNT; state++) {
g_list_free(panel_config.g_task.gradient[state]);
@@ -130,10 +180,18 @@ void cleanup_taskbar()
void init_taskbar()
{
INIT_TIMER(urgent_timer);
INIT_TIMER(thumbnail_update_timer_all);
INIT_TIMER(thumbnail_update_timer_active);
INIT_TIMER(thumbnail_update_timer_tooltip);
if (!panel_config.g_task.has_text && !panel_config.g_task.has_icon) {
panel_config.g_task.has_text = panel_config.g_task.has_icon = 1;
}
if (panel_config.g_task.thumbnail_width < 8)
panel_config.g_task.thumbnail_width = 210;
if (!win_to_task)
win_to_task = g_hash_table_new_full(win_hash, win_compare, free, free_ptr_array);
@@ -180,15 +238,15 @@ void init_taskbar_panel(void *p)
panel->g_taskbar.area.resize_needed = 1;
panel->g_taskbar.area.on_screen = TRUE;
if (panel_horizontal) {
panel->g_taskbar.area.posy = top_border_width(&panel->area) + panel->area.paddingy;
panel->g_taskbar.area.posy = top_border_width(&panel->area) + panel->area.paddingy * panel->scale;
panel->g_taskbar.area.height =
panel->area.height - top_bottom_border_width(&panel->area) - 2 * panel->area.paddingy;
panel->area.height - top_bottom_border_width(&panel->area) - 2 * panel->area.paddingy * panel->scale;
panel->g_taskbar.area_name.posy = panel->g_taskbar.area.posy;
panel->g_taskbar.area_name.height = panel->g_taskbar.area.height;
} else {
panel->g_taskbar.area.posx = left_border_width(&panel->area) + panel->area.paddingy;
panel->g_taskbar.area.posx = left_border_width(&panel->area) + panel->area.paddingy * panel->scale;
panel->g_taskbar.area.width =
panel->area.width - left_right_border_width(&panel->area) - 2 * panel->area.paddingy;
panel->area.width - left_right_border_width(&panel->area) - 2 * panel->area.paddingy * panel->scale;
panel->g_taskbar.area_name.posx = panel->g_taskbar.area.posx;
panel->g_taskbar.area_name.width = panel->g_taskbar.area.width;
}
@@ -238,35 +296,36 @@ void init_taskbar_panel(void *p)
if ((panel->g_task.config_background_mask & (1 << TASK_URGENT)) == 0)
panel->g_task.background[TASK_URGENT] = panel->g_task.background[TASK_ACTIVE];
if (!panel->g_task.maximum_width)
if (!panel->g_task.maximum_width || !panel_horizontal)
panel->g_task.maximum_width = server.monitors[panel->monitor].width;
if (!panel->g_task.maximum_height)
if (!panel->g_task.maximum_height || panel_horizontal)
panel->g_task.maximum_height = server.monitors[panel->monitor].height;
if (panel_horizontal) {
panel->g_task.area.posy = panel->g_taskbar.area.posy +
top_bg_border_width(panel->g_taskbar.background[TASKBAR_NORMAL]) +
panel->g_taskbar.area.paddingy;
panel->g_taskbar.area.paddingy * panel->scale;
panel->g_task.area.width = panel->g_task.maximum_width;
panel->g_task.area.height = panel->g_taskbar.area.height -
top_bottom_bg_border_width(panel->g_taskbar.background[TASKBAR_NORMAL]) -
2 * panel->g_taskbar.area.paddingy;
2 * panel->g_taskbar.area.paddingy * panel->scale;
} else {
panel->g_task.area.posx = panel->g_taskbar.area.posx +
left_bg_border_width(panel->g_taskbar.background[TASKBAR_NORMAL]) +
panel->g_taskbar.area.paddingy;
panel->g_taskbar.area.paddingy * panel->scale;
panel->g_task.area.width = panel->g_taskbar.area.width -
left_right_bg_border_width(panel->g_taskbar.background[TASKBAR_NORMAL]) -
2 * panel->g_taskbar.area.paddingy;
panel->g_task.area.height = panel->g_task.maximum_height;
2 * panel->g_taskbar.area.paddingy * panel->scale;
panel->g_task.area.height = panel->g_task.maximum_height * panel->scale;
}
for (int j = 0; j < TASK_STATE_COUNT; ++j) {
if (!panel->g_task.background[j])
panel->g_task.background[j] = &g_array_index(backgrounds, Background, 0);
if (panel->g_task.background[j]->border.radius > panel->g_task.area.height / 2) {
printf("task%sbackground_id has a too large rounded value. Please fix your tint2rc\n",
j == 0 ? "_" : j == 1 ? "_active_" : j == 2 ? "_iconified_" : "_urgent_");
fprintf(stderr,
"tint2: task%sbackground_id has a too large rounded value. Please fix your tint2rc\n",
j == 0 ? "_" : j == 1 ? "_active_" : j == 2 ? "_iconified_" : "_urgent_");
g_array_append_val(backgrounds, *panel->g_task.background[j]);
panel->g_task.background[j] = &g_array_index(backgrounds, Background, backgrounds->len - 1);
panel->g_task.background[j]->border.radius = panel->g_task.area.height / 2;
@@ -274,9 +333,8 @@ void init_taskbar_panel(void *p)
}
// compute vertical position : text and icon
int height_ink, height, width;
int height, width;
get_text_size2(panel->g_task.font_desc,
&height_ink,
&height,
&width,
panel->area.height,
@@ -285,27 +343,20 @@ void init_taskbar_panel(void *p)
5,
PANGO_WRAP_WORD_CHAR,
PANGO_ELLIPSIZE_END,
FALSE);
panel->g_task.centered ? PANGO_ALIGN_CENTER : PANGO_ALIGN_LEFT,
FALSE,
panel->scale);
panel->g_task.text_posx = left_bg_border_width(panel->g_task.background[0]) + panel->g_task.area.paddingxlr;
panel->g_task.text_height = panel->g_task.area.height - (2 * panel->g_task.area.paddingy);
panel->g_task.text_posx = left_bg_border_width(panel->g_task.background[0]) + panel->g_task.area.paddingxlr * panel->scale;
panel->g_task.text_height =
panel->g_task.area.height - (2 * panel->g_task.area.paddingy * panel->scale) - top_bottom_border_width(&panel->g_task.area);
if (panel->g_task.has_icon) {
panel->g_task.icon_size1 = MIN(MIN(panel->g_task.maximum_width, panel->g_task.maximum_height),
panel->g_task.icon_size1 = MIN(MIN(panel->g_task.maximum_width * panel->scale, panel->g_task.maximum_height * panel->scale),
MIN(panel->g_task.area.width, panel->g_task.area.height)) -
2 * panel->g_task.area.paddingy - MAX(left_right_border_width(&panel->g_task.area),
2 * panel->g_task.area.paddingy * panel->scale - MAX(left_right_border_width(&panel->g_task.area),
top_bottom_border_width(&panel->g_task.area));
panel->g_task.text_posx += panel->g_task.icon_size1 + panel->g_task.area.paddingx;
panel->g_task.text_posx += panel->g_task.icon_size1 + panel->g_task.area.paddingx * panel->scale;
panel->g_task.icon_posy = (panel->g_task.area.height - panel->g_task.icon_size1) / 2;
if (0)
printf("task: icon_size = %d, textx = %f, texth = %f, icony = %d, w = %d, h = %d, maxw = %d, maxh = %d\n",
panel->g_task.icon_size1,
panel->g_task.text_posx,
panel->g_task.text_height,
panel->g_task.icon_posy,
panel->g_task.area.width,
panel->g_task.area.height,
panel->g_task.maximum_width,
panel->g_task.maximum_height);
}
Taskbar *taskbar;
@@ -326,6 +377,22 @@ void init_taskbar_panel(void *p)
}
}
init_taskbarname_panel(panel);
taskbar_start_thumbnail_timer(THUMB_MODE_ALL);
}
void taskbar_start_thumbnail_timer(ThumbnailUpdateMode mode)
{
if (!panel_config.g_task.thumbnail_enabled)
return;
if (debug_thumbnails)
fprintf(stderr, BLUE "tint2: taskbar_start_thumbnail_timer %s" RESET "\n", mode == THUMB_MODE_ACTIVE_WINDOW ? "active" : mode == THUMB_MODE_TOOLTIP_WINDOW ? "tooltip" : "all");
change_timer(mode == THUMB_MODE_ALL ? &thumbnail_update_timer_all :
mode == THUMB_MODE_ACTIVE_WINDOW ? &thumbnail_update_timer_active : &thumbnail_update_timer_tooltip,
true,
mode == THUMB_MODE_TOOLTIP_WINDOW ? 1000 : 500,
mode == THUMB_MODE_ALL ? 10 * 1000 : 0,
taskbar_update_thumbnails,
(void *)(long)mode);
}
void taskbar_init_fonts()
@@ -388,14 +455,67 @@ GPtrArray *get_task_buttons(Window win)
return NULL;
}
static Window *sort_windows = NULL;
int compare_windows(const void *a, const void *b)
{
if (!sort_windows)
return 0;
int ia = *(int *)a;
int ib = *(int *)b;
Window wina = sort_windows[ia];
Window winb = sort_windows[ib];
for (GList *order = taskbar_task_orderings; order; order = order->next) {
int posa = -1;
int posb = -1;
int pos = 0;
for (GList *item = (GList *)order->data; item; item = item->next, pos++) {
Window win = *(Window *)item->data;
if (win == wina)
posa = pos;
if (win == winb)
posb = pos;
}
if (posa >= 0 && posb >= 0) {
return posa - posb;
}
}
return ia - ib;
}
void sort_win_list(Window *windows, int count)
{
int *indices = (int *)calloc(count, sizeof(int));
for (int i = 0; i < count; i++)
indices[i] = i;
sort_windows = windows;
qsort(indices, count, sizeof(int), compare_windows);
Window *result = (Window *)calloc(count, sizeof(Window));
for (int i = 0; i < count; i++)
result[i] = windows[indices[i]];
memcpy(windows, result, count * sizeof(Window));
free(result);
free(indices);
sort_windows = NULL;
}
void taskbar_refresh_tasklist()
{
if (!taskbar_enabled)
return;
// fprintf(stderr, "%s %d:\n", __FUNCTION__, __LINE__);
int num_results;
Window *win = server_get_property(server.root_win, server.atom._NET_CLIENT_LIST, XA_WINDOW, &num_results);
Window *sorted = (Window *)calloc(num_results, sizeof(Window));
memcpy(sorted, win, num_results * sizeof(Window));
if (taskbar_task_orderings) {
sort_win_list(sorted, num_results);
taskbar_clear_orderings();
}
if (!win)
return;
@@ -403,7 +523,7 @@ void taskbar_refresh_tasklist()
for (GList *it = win_list; it; it = it->next) {
int i;
for (i = 0; i < num_results; i++)
if (*((Window *)it->data) == win[i])
if (*((Window *)it->data) == sorted[i])
break;
if (i == num_results)
taskbar_remove_task(it->data);
@@ -412,10 +532,11 @@ void taskbar_refresh_tasklist()
// Add any new
for (int i = 0; i < num_results; i++)
if (!get_task(win[i]))
add_task(win[i]);
if (!get_task(sorted[i]))
add_task(sorted[i]);
XFree(win);
free(sorted);
}
int taskbar_compute_desired_size(void *obj)
@@ -439,7 +560,6 @@ gboolean resize_taskbar(void *obj)
Taskbar *taskbar = (Taskbar *)obj;
Panel *panel = (Panel *)taskbar->area.panel;
// printf("resize_taskbar %d %d\n", taskbar->area.posx, taskbar->area.posy);
if (panel_horizontal) {
relayout_with_constraint(&taskbar->area, panel->g_task.maximum_width);
@@ -454,12 +574,12 @@ gboolean resize_taskbar(void *obj)
}
}
taskbar->text_width = text_width - panel->g_task.text_posx - right_border_width(&panel->g_task.area) -
panel->g_task.area.paddingxlr;
panel->g_task.area.paddingxlr * panel->scale;
} else {
relayout_with_constraint(&taskbar->area, panel->g_task.maximum_height);
relayout_with_constraint(&taskbar->area, panel->g_task.maximum_height * panel->scale);
taskbar->text_width = taskbar->area.width - (2 * panel->g_taskbar.area.paddingy) - panel->g_task.text_posx -
right_border_width(&panel->g_task.area) - panel->g_task.area.paddingxlr;
taskbar->text_width = taskbar->area.width - (2 * panel->g_taskbar.area.paddingy * panel->scale) - panel->g_task.text_posx -
right_border_width(&panel->g_task.area) - panel->g_task.area.paddingxlr * panel->scale;
}
return FALSE;
}
@@ -534,6 +654,15 @@ void set_taskbar_state(Taskbar *taskbar, TaskbarState state)
for (; l; l = l->next)
schedule_redraw((Area *)l->data);
}
if (taskbar_mode == MULTI_DESKTOP && hide_task_diff_desktop) {
GList *l = taskbar->area.children;
if (taskbarname_enabled)
l = l->next;
for (; l; l = l->next) {
Task *task = (Task *)l->data;
set_task_state(task, task->current_state);
}
}
}
schedule_panel_redraw();
}
@@ -606,6 +735,14 @@ gint compare_task_titles(Task *a, Task *b, Taskbar *taskbar)
return strnatcasecmp(a->title ? a->title : "", b->title ? b->title : "");
}
gint compare_task_applications(Task *a, Task *b, Taskbar *taskbar)
{
int trivial = compare_tasks_trivial(a, b, taskbar);
if (trivial != NONTRIVIAL)
return trivial;
return strnatcasecmp(a->application ? a->application : "", b->application ? b->application : "");
}
gint compare_tasks(Task *a, Task *b, Taskbar *taskbar)
{
int trivial = compare_tasks_trivial(a, b, taskbar);
@@ -617,6 +754,8 @@ gint compare_tasks(Task *a, Task *b, Taskbar *taskbar)
return compare_task_centers(a, b, taskbar);
} else if (taskbar_sort_method == TASKBAR_SORT_TITLE) {
return compare_task_titles(a, b, taskbar);
} else if (taskbar_sort_method == TASKBAR_SORT_APPLICATION) {
return compare_task_applications(a, b, taskbar);
} else if (taskbar_sort_method == TASKBAR_SORT_LRU) {
return compare_timespecs(&a->last_activation_time, &b->last_activation_time);
} else if (taskbar_sort_method == TASKBAR_SORT_MRU) {
@@ -688,3 +827,48 @@ void update_minimized_icon_positions(void *p)
}
}
}
void taskbar_update_thumbnails(void *arg)
{
if (!panel_config.g_task.thumbnail_enabled)
return;
ThumbnailUpdateMode mode = (ThumbnailUpdateMode)(long)arg;
if (debug_thumbnails)
fprintf(stderr, BLUE "tint2: taskbar_update_thumbnails %s" RESET "\n", mode == THUMB_MODE_ACTIVE_WINDOW ? "active" : mode == THUMB_MODE_TOOLTIP_WINDOW ? "tooltip" : "all");
double start_time = get_time();
for (int i = 0; i < num_panels; i++) {
Panel *panel = &panels[i];
for (int j = 0; j < panel->num_desktops; j++) {
Taskbar *taskbar = &panel->taskbar[j];
for (GList *c = (taskbar->area.children && taskbarname_enabled) ? taskbar->area.children->next
: taskbar->area.children;
c;
c = c->next) {
Task *t = (Task *)c->data;
if ((mode == THUMB_MODE_ALL && t->current_state == TASK_ACTIVE && !g_list_find(taskbar_thumbnail_jobs_done, t)) || (mode == THUMB_MODE_ACTIVE_WINDOW && t->current_state == TASK_ACTIVE) ||
(mode == THUMB_MODE_TOOLTIP_WINDOW && g_tooltip.mapped && g_tooltip.area == &t->area)) {
task_refresh_thumbnail(t);
if (mode == THUMB_MODE_ALL)
taskbar_thumbnail_jobs_done = g_list_append(taskbar_thumbnail_jobs_done, t);
if (t->thumbnail && mode == THUMB_MODE_TOOLTIP_WINDOW) {
taskbar_start_thumbnail_timer(THUMB_MODE_TOOLTIP_WINDOW);
}
}
if (mode == THUMB_MODE_ALL) {
double now = get_time();
if (now - start_time > 0.030) {
change_timer(&thumbnail_update_timer_all, true, 50, 10 * 1000, taskbar_update_thumbnails, arg);
return;
}
}
}
}
}
if (mode == THUMB_MODE_ALL) {
if (taskbar_thumbnail_jobs_done) {
g_list_free(taskbar_thumbnail_jobs_done);
taskbar_thumbnail_jobs_done = NULL;
change_timer(&thumbnail_update_timer_all, true, 10 * 1000, 10 * 1000, taskbar_update_thumbnails, arg);
}
}
}

View File

@@ -21,10 +21,17 @@ typedef enum TaskbarSortMethod {
TASKBAR_NOSORT = 0,
TASKBAR_SORT_CENTER,
TASKBAR_SORT_TITLE,
TASKBAR_SORT_APPLICATION,
TASKBAR_SORT_LRU,
TASKBAR_SORT_MRU,
} TaskbarSortMethod;
typedef enum ThumbnailUpdateMode {
THUMB_MODE_ACTIVE_WINDOW = 0,
THUMB_MODE_TOOLTIP_WINDOW,
THUMB_MODE_ALL
} ThumbnailUpdateMode;
typedef struct {
Area area;
gchar *name;
@@ -49,6 +56,7 @@ typedef struct GlobalTaskbar {
extern gboolean taskbar_enabled;
extern gboolean taskbar_distribute_size;
extern gboolean hide_task_diff_desktop;
extern gboolean hide_inactive_tasks;
extern gboolean hide_task_diff_monitor;
extern gboolean hide_taskbar_if_empty;
@@ -71,6 +79,7 @@ void init_taskbar_panel(void *p);
gboolean resize_taskbar(void *obj);
void taskbar_default_font_changed();
void taskbar_start_thumbnail_timer(ThumbnailUpdateMode mode);
// Reloads the entire list of tasks from the window manager and recreates the task buttons.
void taskbar_refresh_tasklist();

View File

@@ -134,9 +134,8 @@ int taskbarname_compute_desired_size(void *obj)
{
TaskbarName *taskbar_name = (TaskbarName *)obj;
Panel *panel = (Panel *)taskbar_name->area.panel;
int name_height, name_width, name_height_ink;
int name_height, name_width;
get_text_size2(panel_config.taskbarname_font_desc,
&name_height_ink,
&name_height,
&name_width,
panel->area.height,
@@ -145,12 +144,14 @@ int taskbarname_compute_desired_size(void *obj)
strlen(taskbar_name->name),
PANGO_WRAP_WORD_CHAR,
PANGO_ELLIPSIZE_NONE,
FALSE);
PANGO_ALIGN_CENTER,
FALSE,
panel->scale);
if (panel_horizontal) {
return name_width + 2 * taskbar_name->area.paddingxlr + left_right_border_width(&taskbar_name->area);
return name_width + 2 * taskbar_name->area.paddingxlr * panel->scale + left_right_border_width(&taskbar_name->area);
} else {
return name_height + 2 * taskbar_name->area.paddingxlr + top_bottom_border_width(&taskbar_name->area);
return name_height + 2 * taskbar_name->area.paddingxlr * panel->scale + top_bottom_border_width(&taskbar_name->area);
}
}
@@ -161,9 +162,8 @@ gboolean resize_taskbarname(void *obj)
schedule_redraw(&taskbar_name->area);
int name_height, name_width, name_height_ink;
int name_height, name_width;
get_text_size2(panel_config.taskbarname_font_desc,
&name_height_ink,
&name_height,
&name_width,
panel->area.height,
@@ -172,7 +172,9 @@ gboolean resize_taskbarname(void *obj)
strlen(taskbar_name->name),
PANGO_WRAP_WORD_CHAR,
PANGO_ELLIPSIZE_NONE,
FALSE);
PANGO_ALIGN_CENTER,
FALSE,
panel->scale);
gboolean result = FALSE;
int new_size = taskbarname_compute_desired_size(obj);
@@ -195,11 +197,14 @@ gboolean resize_taskbarname(void *obj)
void draw_taskbarname(void *obj, cairo_t *c)
{
TaskbarName *taskbar_name = obj;
Panel *panel = (Panel *)taskbar_name->area.panel;
Taskbar *taskbar = taskbar_name->area.parent;
Color *config_text = (taskbar->desktop == server.desktop) ? &taskbarname_active_font : &taskbarname_font;
// draw content
PangoLayout *layout = pango_cairo_create_layout(c);
PangoContext *context = pango_cairo_create_context(c);
pango_cairo_context_set_resolution(context, 96 * panel->scale);
PangoLayout *layout = pango_layout_new(context);
pango_layout_set_font_description(layout, panel_config.taskbarname_font_desc);
pango_layout_set_width(layout, taskbar_name->area.width * PANGO_SCALE);
pango_layout_set_alignment(layout, PANGO_ALIGN_CENTER);
@@ -213,4 +218,37 @@ void draw_taskbarname(void *obj, cairo_t *c)
draw_text(layout, c, 0, taskbar_name->posy, config_text, ((Panel *)taskbar_name->area.panel)->font_shadow);
g_object_unref(layout);
g_object_unref(context);
}
void update_desktop_names()
{
if (!taskbarname_enabled)
return;
GSList *list = get_desktop_names();
for (int i = 0; i < num_panels; i++) {
int j;
GSList *l;
for (j = 0, l = list; j < panels[i].num_desktops; j++) {
gchar *name;
if (l) {
name = g_strdup(l->data);
l = l->next;
} else {
name = g_strdup_printf("%d", j + 1);
}
Taskbar *taskbar = &panels[i].taskbar[j];
if (strcmp(name, taskbar->bar_name.name) != 0) {
g_free(taskbar->bar_name.name);
taskbar->bar_name.name = name;
taskbar->bar_name.area.resize_needed = 1;
} else {
g_free(name);
}
}
}
for (GSList *l = list; l; l = l->next)
g_free(l->data);
g_slist_free(list);
schedule_panel_redraw();
}

View File

@@ -23,4 +23,6 @@ gboolean resize_taskbarname(void *obj);
void taskbarname_default_font_changed();
void update_desktop_names();
#endif

2183
src/tint.c

File diff suppressed because it is too large Load Diff

View File

@@ -20,10 +20,16 @@ include_directories( ../util
${RSVG_INCLUDE_DIRS} )
set(SOURCES ../util/common.c
../util/bt.c
../util/strnatcmp.c
../util/cache.c
../util/timer.c
../util/test.c
../util/print.c
../util/signals.c
../config.c
../server.c
../util/server.c
../util/strlcat.c
../launcher/apps-common.c
../launcher/icon-theme-common.c
md4.c

View File

@@ -6,7 +6,7 @@ GtkWidget *current_background, *background_fill_color, *background_border_color,
*background_fill_color_over, *background_border_color_over, *background_gradient_over, *background_fill_color_press,
*background_border_color_press, *background_gradient_press, *background_border_width, *background_corner_radius,
*background_border_sides_top, *background_border_sides_bottom, *background_border_sides_left,
*background_border_sides_right;
*background_border_sides_right, *background_border_content_tint_weight, *background_fill_content_tint_weight;
GtkWidget *create_background_combo(const char *label)
{
@@ -115,7 +115,9 @@ void create_background(GtkWidget *parent)
GTK_TYPE_BOOL,
GTK_TYPE_BOOL,
GTK_TYPE_BOOL,
GTK_TYPE_BOOL);
GTK_TYPE_BOOL,
GTK_TYPE_DOUBLE,
GTK_TYPE_DOUBLE);
GtkWidget *table, *label, *button;
int row, col;
@@ -175,6 +177,19 @@ void create_background(GtkWidget *parent)
col++;
gtk_tooltips_set_tip(tooltips, background_fill_color, _("The fill color of the current background"), NULL);
row++, col = 2;
label = gtk_label_new(_("Fill tint"));
gtk_misc_set_alignment(GTK_MISC(label), 0, 0);
gtk_widget_show(label);
gtk_table_attach(GTK_TABLE(table), label, col, col + 1, row, row + 1, GTK_FILL, 0, 0, 0);
col++;
background_fill_content_tint_weight = gtk_spin_button_new_with_range(0, 100, 1);
gtk_widget_show(background_fill_content_tint_weight);
gtk_table_attach(GTK_TABLE(table), background_fill_content_tint_weight, col, col + 1, row, row + 1, GTK_FILL, 0, 0, 0);
col++;
gtk_tooltips_set_tip(tooltips, background_fill_content_tint_weight, _("How much the border color should be tinted with the content color"), NULL);
row++, col = 2;
label = gtk_label_new(_("Border color"));
gtk_misc_set_alignment(GTK_MISC(label), 0, 0);
@@ -189,6 +204,19 @@ void create_background(GtkWidget *parent)
col++;
gtk_tooltips_set_tip(tooltips, background_border_color, _("The border color of the current background"), NULL);
row++, col = 2;
label = gtk_label_new(_("Border tint"));
gtk_misc_set_alignment(GTK_MISC(label), 0, 0);
gtk_widget_show(label);
gtk_table_attach(GTK_TABLE(table), label, col, col + 1, row, row + 1, GTK_FILL, 0, 0, 0);
col++;
background_border_content_tint_weight = gtk_spin_button_new_with_range(0, 100, 1);
gtk_widget_show(background_border_content_tint_weight);
gtk_table_attach(GTK_TABLE(table), background_border_content_tint_weight, col, col + 1, row, row + 1, GTK_FILL, 0, 0, 0);
col++;
gtk_tooltips_set_tip(tooltips, background_border_content_tint_weight, _("How much the border color should be tinted with the content color"), NULL);
row++, col = 2;
label = gtk_label_new(_("Gradient"));
gtk_misc_set_alignment(GTK_MISC(label), 0, 0);
@@ -366,6 +394,8 @@ void create_background(GtkWidget *parent)
g_signal_connect(G_OBJECT(background_border_sides_bottom), "toggled", G_CALLBACK(background_update), NULL);
g_signal_connect(G_OBJECT(background_border_sides_left), "toggled", G_CALLBACK(background_update), NULL);
g_signal_connect(G_OBJECT(background_border_sides_right), "toggled", G_CALLBACK(background_update), NULL);
g_signal_connect(G_OBJECT(background_border_content_tint_weight), "value-changed", G_CALLBACK(background_update), NULL);
g_signal_connect(G_OBJECT(background_fill_content_tint_weight), "value-changed", G_CALLBACK(background_update), NULL);
change_paragraph(parent);
}
@@ -750,6 +780,9 @@ void background_update(GtkWidget *widget, gpointer data)
r = gtk_spin_button_get_value(GTK_SPIN_BUTTON(background_corner_radius));
b = gtk_spin_button_get_value(GTK_SPIN_BUTTON(background_border_width));
double fill_weight = gtk_spin_button_get_value(GTK_SPIN_BUTTON(background_fill_content_tint_weight));
double border_weight = gtk_spin_button_get_value(GTK_SPIN_BUTTON(background_border_content_tint_weight));
gboolean sideTop = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(background_border_sides_top));
gboolean sideBottom = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(background_border_sides_bottom));
gboolean sideLeft = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(background_border_sides_left));
@@ -836,6 +869,10 @@ void background_update(GtkWidget *widget, gpointer data)
sideLeft,
bgColBorderSidesRight,
sideRight,
bgColFillWeight,
fill_weight,
bgColBorderWeight,
border_weight,
-1);
background_update_image(index);
}
@@ -862,6 +899,8 @@ void current_background_changed(GtkWidget *widget, gpointer data)
gtk_widget_set_sensitive(background_border_sides_left, index > 0);
gtk_widget_set_sensitive(background_border_sides_right, index > 0);
gtk_widget_set_sensitive(background_corner_radius, index > 0);
gtk_widget_set_sensitive(background_border_content_tint_weight, index > 0);
gtk_widget_set_sensitive(background_fill_content_tint_weight, index > 0);
background_updates_disabled = TRUE;
@@ -875,6 +914,9 @@ void current_background_changed(GtkWidget *widget, gpointer data)
int r;
int b;
double fill_weight;
double border_weight;
gboolean sideTop;
gboolean sideBottom;
gboolean sideLeft;
@@ -938,6 +980,10 @@ void current_background_changed(GtkWidget *widget, gpointer data)
&sideLeft,
bgColBorderSidesRight,
&sideRight,
bgColFillWeight,
&fill_weight,
bgColBorderWeight,
&border_weight,
-1);
gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(background_border_sides_top), sideTop);
@@ -968,6 +1014,9 @@ void current_background_changed(GtkWidget *widget, gpointer data)
gtk_spin_button_set_value(GTK_SPIN_BUTTON(background_border_width), b);
gtk_spin_button_set_value(GTK_SPIN_BUTTON(background_corner_radius), r);
gtk_spin_button_set_value(GTK_SPIN_BUTTON(background_fill_content_tint_weight), fill_weight);
gtk_spin_button_set_value(GTK_SPIN_BUTTON(background_border_content_tint_weight), border_weight);
g_boxed_free(GDK_TYPE_COLOR, fillColor);
g_boxed_free(GDK_TYPE_COLOR, borderColor);
g_boxed_free(GDK_TYPE_COLOR, fillColorOver);

View File

@@ -100,7 +100,7 @@ char *file_name_from_path(const char *filepath)
void make_backup(const char *filepath)
{
gchar *backup_path = g_strdup_printf("%s.backup.%ld", filepath, time(NULL));
gchar *backup_path = g_strdup_printf("%s.backup.%lld", filepath, (long long)time(NULL));
copy_file(filepath, backup_path);
g_free(backup_path);
}
@@ -227,7 +227,7 @@ int main(int argc, char **argv)
{
gchar *tint2_config_dir = g_build_filename(g_get_user_config_dir(), "tint2", NULL);
if (!g_file_test(tint2_config_dir, G_FILE_TEST_IS_DIR))
g_mkdir(tint2_config_dir, 0700);
g_mkdir_with_parents(tint2_config_dir, 0700);
g_free(tint2_config_dir);
}

File diff suppressed because it is too large Load Diff

2433
src/tint2conf/po/es.po Normal file

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,36 @@
#!/bin/bash
set -e
set -x
find .. -name '*.c' | sort -r | xargs xgettext --keyword=_ --language=C --output=tint2conf.pot -
sed -i "s/PACKAGE VERSION/tint2conf $(../../../get_version.sh | head -n1)/g" tint2conf.pot
sed -i "s/CHARSET/UTF-8/g" tint2conf.pot
for f in *.po
do
lang=$(basename $f .po)
echo $lang
msgmerge -i -o $lang.pox $lang.po tint2conf.pot
cat ${lang}.pox > ${lang}.po
rm ${lang}.pox
done
set +e
set +x
echo "Status:"
for f in *.po
do
lang=$(basename $f .po)
fuzzy=$(cat ${lang}.po | grep -A2 "#, fuzzy")
missing=$(cat ${lang}.po | grep -B1 'msgstr ""')
if [ -z "$fuzzy" ] && [ -z "$missing" ]
then
echo $lang ": Up to date"
else
count=$(( $(echo -e "$fuzzy" "\n" "$missing" | grep "^--$" | wc -l) + 1))
echo "${lang}: Translation incomplete: ${count} strings to be updated. See ${lang}.todo"
echo "$fuzzy" > ${lang}.todo
echo "$missing" >> ${lang}.todo
fi
done

View File

@@ -20,7 +20,9 @@
#include "gui.h"
#include "background_gui.h"
#include "gradient_gui.h"
#include "strlcat.h"
GtkWidget *scale_relative_to_dpi, *scale_relative_to_screen_height;
GtkWidget *panel_width, *panel_height, *panel_margin_x, *panel_margin_y, *panel_padding_x, *panel_padding_y,
*panel_spacing;
GtkWidget *panel_wm_menu, *panel_dock, *panel_autohide, *panel_autohide_show_time, *panel_autohide_hide_time,
@@ -31,7 +33,7 @@ GtkWidget *panel_window_name, *disable_transparency;
GtkWidget *panel_mouse_effects;
GtkWidget *mouse_hover_icon_opacity, *mouse_hover_icon_saturation, *mouse_hover_icon_brightness;
GtkWidget *mouse_pressed_icon_opacity, *mouse_pressed_icon_saturation, *mouse_pressed_icon_brightness;
GtkWidget *panel_primary_monitor_first, *panel_shrink;
GtkWidget *panel_shrink;
GtkListStore *panel_items, *all_items;
GtkWidget *panel_items_view, *all_items_view;
@@ -44,7 +46,7 @@ GtkWidget *notebook;
// taskbar
GtkWidget *taskbar_show_desktop, *taskbar_show_name, *taskbar_padding_x, *taskbar_padding_y, *taskbar_spacing;
GtkWidget *taskbar_hide_inactive_tasks, *taskbar_hide_diff_monitor;
GtkWidget *taskbar_hide_inactive_tasks, *taskbar_hide_diff_monitor, *taskbar_hide_diff_desktop;
GtkWidget *taskbar_name_padding_x, *taskbar_name_padding_y, *taskbar_name_inactive_color, *taskbar_name_active_color;
GtkWidget *taskbar_name_font, *taskbar_name_font_set;
GtkWidget *taskbar_active_background, *taskbar_inactive_background;
@@ -80,10 +82,10 @@ GtkWidget *clock_font_line1, *clock_font_line1_set, *clock_font_line2, *clock_fo
GtkWidget *clock_background;
// battery
GtkWidget *battery_hide_if_higher, *battery_alert_if_lower, *battery_alert_cmd;
GtkWidget *battery_hide_if_higher, *battery_alert_if_lower, *battery_alert_cmd, *battery_alert_full_cmd;
GtkWidget *battery_padding_x, *battery_padding_y;
GtkWidget *battery_font_line1, *battery_font_line1_set, *battery_font_line2, *battery_font_line2_set,
*battery_font_color;
*battery_font_color, *battery_format1, *battery_format2;
GtkWidget *battery_background;
GtkWidget *battery_tooltip;
GtkWidget *battery_left_command, *battery_mclick_command, *battery_right_command, *battery_uwheel_command,
@@ -97,7 +99,7 @@ GtkWidget *systray_background, *systray_monitor, *systray_name_filter;
// tooltip
GtkWidget *tooltip_padding_x, *tooltip_padding_y, *tooltip_font, *tooltip_font_set, *tooltip_font_color;
GtkWidget *tooltip_task_show, *tooltip_show_after, *tooltip_hide_after;
GtkWidget *tooltip_task_show, *tooltip_show_after, *tooltip_hide_after, *tooltip_task_thumbnail, *tooltip_task_thumbnail_size;
GtkWidget *clock_format_tooltip, *clock_tmz_tooltip;
GtkWidget *tooltip_background;
@@ -180,7 +182,7 @@ void applyClicked(GtkWidget *widget, gpointer data)
gchar *filepath = get_current_theme_path();
if (filepath) {
if (config_is_manual(filepath)) {
gchar *backup_path = g_strdup_printf("%s.backup.%ld", filepath, time(NULL));
gchar *backup_path = g_strdup_printf("%s.backup.%lld", filepath, (long long)time(NULL));
copy_file(filepath, backup_path);
g_free(backup_path);
}
@@ -477,6 +479,7 @@ void create_panel(GtkWidget *parent)
gtk_table_attach(GTK_TABLE(table), panel_combo_monitor, col, col + 1, row, row + 1, GTK_FILL, 0, 0, 0);
col++;
gtk_combo_box_append_text(GTK_COMBO_BOX(panel_combo_monitor), _("All"));
gtk_combo_box_append_text(GTK_COMBO_BOX(panel_combo_monitor), _("Primary"));
gtk_combo_box_append_text(GTK_COMBO_BOX(panel_combo_monitor), _("1"));
gtk_combo_box_append_text(GTK_COMBO_BOX(panel_combo_monitor), _("2"));
gtk_combo_box_append_text(GTK_COMBO_BOX(panel_combo_monitor), _("3"));
@@ -486,24 +489,6 @@ void create_panel(GtkWidget *parent)
gtk_combo_box_set_active(GTK_COMBO_BOX(panel_combo_monitor), 0);
gtk_tooltips_set_tip(tooltips, panel_combo_monitor, _("The monitor on which the panel is placed"), NULL);
row++;
col = 2;
label = gtk_label_new(_("Primary monitor first"));
gtk_misc_set_alignment(GTK_MISC(label), 0, 0);
gtk_widget_show(label);
gtk_table_attach(GTK_TABLE(table), label, col, col + 1, row, row + 1, GTK_FILL, 0, 0, 0);
col++;
panel_primary_monitor_first = gtk_check_button_new();
gtk_widget_show(panel_primary_monitor_first);
gtk_table_attach(GTK_TABLE(table), panel_primary_monitor_first, col, col + 1, row, row + 1, GTK_FILL, 0, 0, 0);
col++;
gtk_tooltips_set_tip(tooltips,
panel_primary_monitor_first,
_("If enabled, the primary monitor will have index 1 in the monitor list even if it is not "
"top-left."),
NULL);
row++;
col = 2;
label = gtk_label_new(_("Length"));
@@ -616,6 +601,32 @@ void create_panel(GtkWidget *parent)
"for centered panels, it is evenly distributed on both sides of the panel."),
NULL);
row++;
col = 2;
label = gtk_label_new(_("Scale relative to DPI"));
gtk_misc_set_alignment(GTK_MISC(label), 0, 0);
gtk_widget_show(label);
gtk_table_attach(GTK_TABLE(table), label, col, col + 1, row, row + 1, GTK_FILL, 0, 0, 0);
col++;
scale_relative_to_dpi = gtk_spin_button_new_with_range(0, 9000, 1);
gtk_widget_show(scale_relative_to_dpi);
gtk_table_attach(GTK_TABLE(table), scale_relative_to_dpi, col, col + 1, row, row + 1, GTK_FILL, 0, 0, 0);
col++;
row++;
col = 2;
label = gtk_label_new(_("Scale relative to screen height"));
gtk_misc_set_alignment(GTK_MISC(label), 0, 0);
gtk_widget_show(label);
gtk_table_attach(GTK_TABLE(table), label, col, col + 1, row, row + 1, GTK_FILL, 0, 0, 0);
col++;
scale_relative_to_screen_height = gtk_spin_button_new_with_range(0, 9000, 1);
gtk_widget_show(scale_relative_to_screen_height);
gtk_table_attach(GTK_TABLE(table), scale_relative_to_screen_height, col, col + 1, row, row + 1, GTK_FILL, 0, 0, 0);
col++;
change_paragraph(parent);
label = gtk_label_new(_("<b>Appearance</b>"));
@@ -1273,7 +1284,8 @@ gboolean panel_contains(const char *value)
char *get_panel_items()
{
char *result = calloc(1, 256 * sizeof(char));
size_t buf_size = 256;
char *result = calloc(buf_size, 1);
GtkTreeModel *model = GTK_TREE_MODEL(panel_items);
GtkTreeIter i;
@@ -1284,7 +1296,7 @@ char *get_panel_items()
while (1) {
gchar *v;
gtk_tree_model_get(model, &i, itemsColValue, &v, -1);
strcat(result, v);
strlcat(result, v, buf_size);
if (!gtk_tree_model_iter_next(model, &i)) {
break;
@@ -1329,19 +1341,19 @@ void set_panel_items(const char *items)
} else if (v == ':') {
separator_index++;
buffer[0] = 0;
sprintf(buffer, "%s %d", _("Separator"), separator_index + 1);
snprintf(buffer, sizeof(buffer), "%s %d", _("Separator"), separator_index + 1);
name = buffer;
value = ":";
} else if (v == 'E') {
execp_index++;
buffer[0] = 0;
sprintf(buffer, "%s %d", _("Executor"), execp_index + 1);
snprintf(buffer, sizeof(buffer), "%s %d", _("Executor"), execp_index + 1);
name = buffer;
value = "E";
} else if (v == 'P') {
button_index++;
buffer[0] = 0;
sprintf(buffer, "%s %d", _("Button"), button_index + 1);
snprintf(buffer, sizeof(buffer), "%s %d", _("Button"), button_index + 1);
name = buffer;
value = "P";
} else {
@@ -1856,7 +1868,7 @@ void load_desktop_file(const char *file, gboolean selected)
if (pixbuf)
g_object_unref(pixbuf);
} else {
printf("Could not load %s\n", file);
fprintf(stderr, "tint2: Could not load %s\n", file);
GdkPixbuf *pixbuf = load_icon(DEFAULT_ICON);
GtkTreeIter iter;
gtk_list_store_append(store, &iter);
@@ -1914,7 +1926,7 @@ void load_desktop_entry(const char *file, GList **entries)
DesktopEntry *entry = calloc(1, sizeof(DesktopEntry));
if (!read_desktop_file(file, entry))
printf("Could not load %s\n", file);
fprintf(stderr, "tint2: Could not load %s\n", file);
if (entry->hidden_from_menus) {
free(entry);
return;
@@ -2497,7 +2509,7 @@ void create_launcher(GtkWidget *parent, GtkWindow *window)
change_paragraph(parent);
fprintf(stderr, "Loading icon themes\n");
fprintf(stderr, "tint2: Loading icon themes\n");
GList *themes = NULL;
const GSList *location;
for (location = get_icon_locations(); location; location = g_slist_next(location)) {
@@ -2526,9 +2538,9 @@ void create_launcher(GtkWidget *parent, GtkWindow *window)
free_icon_theme((IconTheme *)l->data);
}
g_list_free(themes);
fprintf(stderr, "Icon themes loaded\n");
fprintf(stderr, "tint2: Icon themes loaded\n");
fprintf(stderr, "Loading .desktop files\n");
fprintf(stderr, "tint2: Loading .desktop files\n");
GList *entries = NULL;
for (location = get_apps_locations(); location; location = g_slist_next(location)) {
const gchar *path = (gchar *)location->data;
@@ -2545,7 +2557,7 @@ void create_launcher(GtkWidget *parent, GtkWindow *window)
icon_theme_changed(window);
load_icons(launcher_apps);
load_icons(all_apps);
fprintf(stderr, "Desktop files loaded\n");
fprintf(stderr, "tint2: Desktop files loaded\n");
}
void create_taskbar(GtkWidget *parent)
@@ -2651,6 +2663,19 @@ void create_taskbar(GtkWidget *parent)
"This behavior is enabled automatically if the panel monitor is set to 'All'."),
NULL);
col = 2;
row++;
label = gtk_label_new(_("Hide tasks from different desktops"));
gtk_misc_set_alignment(GTK_MISC(label), 0, 0);
gtk_widget_show(label);
gtk_table_attach(GTK_TABLE(table), label, col, col + 1, row, row + 1, GTK_FILL, 0, 0, 0);
col++;
taskbar_hide_diff_desktop = gtk_check_button_new();
gtk_widget_show(taskbar_hide_diff_desktop);
gtk_table_attach(GTK_TABLE(table), taskbar_hide_diff_desktop, col, col + 1, row, row + 1, GTK_FILL, 0, 0, 0);
col++;
col = 2;
row++;
label = gtk_label_new(_("Always show all desktop tasks"));
@@ -2693,6 +2718,7 @@ void create_taskbar(GtkWidget *parent)
col++;
gtk_combo_box_append_text(GTK_COMBO_BOX(taskbar_sort_order), _("None"));
gtk_combo_box_append_text(GTK_COMBO_BOX(taskbar_sort_order), _("By title"));
gtk_combo_box_append_text(GTK_COMBO_BOX(taskbar_sort_order), _("By application"));
gtk_combo_box_append_text(GTK_COMBO_BOX(taskbar_sort_order), _("By center"));
gtk_combo_box_append_text(GTK_COMBO_BOX(taskbar_sort_order), _("Most recently used first"));
gtk_combo_box_append_text(GTK_COMBO_BOX(taskbar_sort_order), _("Most recently used last"));
@@ -2703,6 +2729,7 @@ void create_taskbar(GtkWidget *parent)
"'None' means that new tasks are added to the end, and the user can also reorder task "
"buttons by mouse dragging. \n"
"'By title' means that tasks are sorted by their window titles. \n"
"'By application' means that tasks are sorted by their application names. \n"
"'By center' means that tasks are sorted geometrically by their window centers."),
NULL);
@@ -3292,6 +3319,36 @@ void create_task(GtkWidget *parent)
"over task buttons."),
NULL);
row++, col = 2;
label = gtk_label_new(_("Thumbnails"));
gtk_misc_set_alignment(GTK_MISC(label), 0, 0);
gtk_widget_show(label);
gtk_table_attach(GTK_TABLE(table), label, col, col + 1, row, row + 1, GTK_FILL, 0, 0, 0);
col++;
tooltip_task_thumbnail = gtk_check_button_new();
gtk_widget_show(tooltip_task_thumbnail);
gtk_table_attach(GTK_TABLE(table), tooltip_task_thumbnail, col, col + 1, row, row + 1, GTK_FILL, 0, 0, 0);
col++;
gtk_tooltips_set_tip(tooltips,
tooltip_task_thumbnail,
_("If enabled, a tooltip showing the window thumbnail is displayed when the mouse cursor moves "
"over task buttons."),
NULL);
row++, col = 2;
label = gtk_label_new(_("Thumbnail size"));
gtk_misc_set_alignment(GTK_MISC(label), 0, 0);
gtk_widget_show(label);
gtk_table_attach(GTK_TABLE(table), label, col, col + 1, row, row + 1, GTK_FILL, 0, 0, 0);
col++;
tooltip_task_thumbnail_size = gtk_spin_button_new_with_range(8, 9000, 1);
gtk_spin_button_set_value(GTK_SPIN_BUTTON(tooltip_task_thumbnail_size), 210);
gtk_widget_show(tooltip_task_thumbnail_size);
gtk_table_attach(GTK_TABLE(table), tooltip_task_thumbnail_size, col, col + 1, row, row + 1, GTK_FILL, 0, 0, 0);
col++;
row++, col = 2;
label = gtk_label_new(_("Maximum width"));
gtk_misc_set_alignment(GTK_MISC(label), 0, 0);
@@ -4071,7 +4128,7 @@ void create_separator(GtkWidget *notebook, int i)
Separator *separator = &g_array_index(separators, Separator, i);
separator->name[0] = 0;
sprintf(separator->name, "%s %d", _("Separator"), i + 1);
snprintf(separator->name, sizeof(separator->name), "%s %d", _("Separator"), i + 1);
separator->page_label = gtk_label_new(separator->name);
gtk_widget_show(separator->page_label);
separator->page_separator = gtk_vbox_new(FALSE, DEFAULT_HOR_SPACING);
@@ -4197,7 +4254,7 @@ void create_execp(GtkWidget *notebook, int i)
Executor *executor = &g_array_index(executors, Executor, i);
executor->name[0] = 0;
sprintf(executor->name, "%s %d", _("Executor"), i + 1);
snprintf(executor->name, sizeof(executor->name), "%s %d", _("Executor"), i + 1);
executor->page_label = gtk_label_new(executor->name);
gtk_widget_show(executor->page_label);
executor->page_execp = gtk_vbox_new(FALSE, DEFAULT_HOR_SPACING);
@@ -4618,7 +4675,7 @@ void create_button(GtkWidget *notebook, int i)
Button *button = &g_array_index(buttons, Button, i);
button->name[0] = 0;
sprintf(button->name, "%s %d", _("Button"), i + 1);
snprintf(button->name, sizeof(button->name), "%s %d", _("Button"), i + 1);
button->page_label = gtk_label_new(button->name);
gtk_widget_show(button->page_label);
button->page_button = gtk_vbox_new(FALSE, DEFAULT_HOR_SPACING);
@@ -5007,7 +5064,7 @@ void separator_update_indices()
{
for (int i = 0; i < separators->len; i++) {
Separator *separator = &g_array_index(separators, Separator, i);
sprintf(separator->name, "%s %d", _("Separator"), i + 1);
snprintf(separator->name, sizeof(separator->name), "%s %d", _("Separator"), i + 1);
gtk_label_set_text(GTK_LABEL(separator->page_label), separator->name);
}
@@ -5025,7 +5082,7 @@ void separator_update_indices()
separator_index++;
char buffer[256];
buffer[0] = 0;
sprintf(buffer, "%s %d", _("Separator"), separator_index + 1);
snprintf(buffer, sizeof(buffer), "%s %d", _("Separator"), separator_index + 1);
gtk_list_store_set(panel_items, &iter, itemsColName, buffer, -1);
}
@@ -5039,7 +5096,7 @@ void execp_update_indices()
{
for (int i = 0; i < executors->len; i++) {
Executor *executor = &g_array_index(executors, Executor, i);
sprintf(executor->name, "%s %d", _("Executor"), i + 1);
snprintf(executor->name, sizeof(executor->name), "%s %d", _("Executor"), i + 1);
gtk_label_set_text(GTK_LABEL(executor->page_label), executor->name);
}
@@ -5057,7 +5114,7 @@ void execp_update_indices()
execp_index++;
char buffer[256];
buffer[0] = 0;
sprintf(buffer, "%s %d", _("Executor"), execp_index + 1);
snprintf(buffer, sizeof(buffer), "%s %d", _("Executor"), execp_index + 1);
gtk_list_store_set(panel_items, &iter, itemsColName, buffer, -1);
}
@@ -5071,7 +5128,7 @@ void button_update_indices()
{
for (int i = 0; i < buttons->len; i++) {
Button *button = &g_array_index(buttons, Button, i);
sprintf(button->name, "%s %d", _("Button"), i + 1);
snprintf(button->name, sizeof(button->name), "%s %d", _("Button"), i + 1);
gtk_label_set_text(GTK_LABEL(button->page_label), button->name);
}
@@ -5089,7 +5146,7 @@ void button_update_indices()
button_index++;
char buffer[256];
buffer[0] = 0;
sprintf(buffer, "%s %d", _("Button"), button_index + 1);
snprintf(buffer, sizeof(buffer), "%s %d", _("Button"), button_index + 1);
gtk_list_store_set(panel_items, &iter, itemsColName, buffer, -1);
}
@@ -5140,8 +5197,8 @@ void create_systemtray(GtkWidget *parent)
_("Specifies the order used to arrange the system tray icons. \n"
"'Ascending' means that icons are sorted in ascending order of their window names. \n"
"'Descending' means that icons are sorted in descending order of their window names. \n"
"'Left to right' means that icons are always added to the left. \n"
"'Right to left' means that icons are always added to the right."),
"'Left to right' means that icons are always added to the right. \n"
"'Right to left' means that icons are always added to the left."),
NULL);
row++;
@@ -5156,6 +5213,7 @@ void create_systemtray(GtkWidget *parent)
gtk_widget_show(systray_monitor);
gtk_table_attach(GTK_TABLE(table), systray_monitor, col, col + 1, row, row + 1, GTK_FILL, 0, 0, 0);
col++;
gtk_combo_box_append_text(GTK_COMBO_BOX(systray_monitor), _("Primary"));
gtk_combo_box_append_text(GTK_COMBO_BOX(systray_monitor), _("1"));
gtk_combo_box_append_text(GTK_COMBO_BOX(systray_monitor), _("2"));
gtk_combo_box_append_text(GTK_COMBO_BOX(systray_monitor), _("3"));
@@ -5419,6 +5477,19 @@ void create_battery(GtkWidget *parent)
_("Command to be executed when the alert threshold is reached."),
NULL);
row++, col = 2;
label = gtk_label_new(_("Battery full command"));
gtk_misc_set_alignment(GTK_MISC(label), 0, 0);
gtk_widget_show(label);
gtk_table_attach(GTK_TABLE(table), label, col, col + 1, row, row + 1, GTK_FILL, 0, 0, 0);
col++;
battery_alert_full_cmd = gtk_entry_new();
gtk_widget_show(battery_alert_full_cmd);
gtk_entry_set_width_chars(GTK_ENTRY(battery_alert_full_cmd), 50);
gtk_table_attach(GTK_TABLE(table), battery_alert_full_cmd, col, col + 3, row, row + 1, GTK_FILL, 0, 0, 0);
col++;
change_paragraph(parent);
label = gtk_label_new(_("<b>AC connection events</b>"));
@@ -5736,6 +5807,46 @@ void create_battery(GtkWidget *parent)
_("Specifies the font clor used to display the battery text."),
NULL);
row++, col = 2;
label = gtk_label_new(_("First line format"));
gtk_misc_set_alignment(GTK_MISC(label), 0, 0);
gtk_widget_show(label);
gtk_table_attach(GTK_TABLE(table), label, col, col + 1, row, row + 1, GTK_FILL, 0, 0, 0);
col++;
battery_format1 = gtk_entry_new();
gtk_widget_show(battery_format1);
gtk_entry_set_width_chars(GTK_ENTRY(battery_format1), 16);
gtk_table_attach(GTK_TABLE(table), battery_format1, col, col + 1, row, row + 1, GTK_FILL, 0, 0, 0);
col++;
char *bat_format_spec = "Format specification:\n"
" %s: State (charging, discharging, full, unknown).\n"
" %m: Minutes left until completely charged/discharged (estimated).\n"
" %h: Hours left until completely charged/discharged (estimated).\n"
" %t: Time left. Shows \"hrs:mins\" when charging/discharging, or \"Full\".\n"
" %p: Percentage. Includes the % sign.";
gtk_tooltips_set_tip(tooltips,
battery_format1,
_(bat_format_spec),
NULL);
row++, col = 2;
label = gtk_label_new(_("Second line format"));
gtk_misc_set_alignment(GTK_MISC(label), 0, 0);
gtk_widget_show(label);
gtk_table_attach(GTK_TABLE(table), label, col, col + 1, row, row + 1, GTK_FILL, 0, 0, 0);
col++;
battery_format2 = gtk_entry_new();
gtk_widget_show(battery_format2);
gtk_entry_set_width_chars(GTK_ENTRY(battery_format2), 16);
gtk_table_attach(GTK_TABLE(table), battery_format2, col, col + 1, row, row + 1, GTK_FILL, 0, 0, 0);
col++;
gtk_tooltips_set_tip(tooltips,
battery_format2,
_(bat_format_spec),
NULL);
change_paragraph(parent);
}

View File

@@ -9,6 +9,7 @@
#include "../launcher/icon-theme-common.h"
// panel
extern GtkWidget *scale_relative_to_dpi, *scale_relative_to_screen_height;
extern GtkWidget *panel_width, *panel_height, *panel_margin_x, *panel_margin_y, *panel_padding_x, *panel_padding_y,
*panel_spacing;
extern GtkWidget *panel_wm_menu, *panel_dock, *panel_autohide, *panel_autohide_show_time, *panel_autohide_hide_time,
@@ -19,7 +20,7 @@ extern GtkWidget *panel_window_name, *disable_transparency;
extern GtkWidget *panel_mouse_effects;
extern GtkWidget *mouse_hover_icon_opacity, *mouse_hover_icon_saturation, *mouse_hover_icon_brightness;
extern GtkWidget *mouse_pressed_icon_opacity, *mouse_pressed_icon_saturation, *mouse_pressed_icon_brightness;
extern GtkWidget *panel_primary_monitor_first, *panel_shrink;
extern GtkWidget *panel_shrink;
enum { itemsColName = 0, itemsColValue, itemsNumCols };
extern GtkListStore *panel_items, *all_items;
@@ -49,7 +50,7 @@ extern GtkWidget *panel_background;
// taskbar
extern GtkWidget *taskbar_show_desktop, *taskbar_show_name, *taskbar_padding_x, *taskbar_padding_y, *taskbar_spacing;
extern GtkWidget *taskbar_hide_inactive_tasks, *taskbar_hide_diff_monitor;
extern GtkWidget *taskbar_hide_inactive_tasks, *taskbar_hide_diff_monitor, *taskbar_hide_diff_desktop;
extern GtkWidget *taskbar_name_padding_x, *taskbar_name_padding_y, *taskbar_name_inactive_color,
*taskbar_name_active_color;
extern GtkWidget *taskbar_name_font, *taskbar_name_font_set;
@@ -88,10 +89,10 @@ extern GtkWidget *clock_font_line1, *clock_font_line1_set, *clock_font_line2, *c
extern GtkWidget *clock_background;
// battery
extern GtkWidget *battery_hide_if_higher, *battery_alert_if_lower, *battery_alert_cmd;
extern GtkWidget *battery_hide_if_higher, *battery_alert_if_lower, *battery_alert_cmd, *battery_alert_full_cmd;
extern GtkWidget *battery_padding_x, *battery_padding_y;
extern GtkWidget *battery_font_line1, *battery_font_line1_set, *battery_font_line2, *battery_font_line2_set,
*battery_font_color;
*battery_font_color, *battery_format1, *battery_format2;
extern GtkWidget *battery_background;
extern GtkWidget *battery_tooltip;
extern GtkWidget *battery_left_command, *battery_mclick_command, *battery_right_command, *battery_uwheel_command,
@@ -105,7 +106,7 @@ extern GtkWidget *systray_background, *systray_monitor, *systray_name_filter;
// tooltip
extern GtkWidget *tooltip_padding_x, *tooltip_padding_y, *tooltip_font, *tooltip_font_set, *tooltip_font_color;
extern GtkWidget *tooltip_task_show, *tooltip_show_after, *tooltip_hide_after;
extern GtkWidget *tooltip_task_show, *tooltip_show_after, *tooltip_hide_after, *tooltip_task_thumbnail, *tooltip_task_thumbnail_size;
extern GtkWidget *clock_format_tooltip, *clock_tmz_tooltip;
extern GtkWidget *tooltip_background;
@@ -203,6 +204,8 @@ enum {
bgColBorderSidesBottom,
bgColBorderSidesLeft,
bgColBorderSidesRight,
bgColFillWeight,
bgColBorderWeight,
bgNumCols
};
@@ -211,7 +214,7 @@ extern GtkWidget *current_background, *background_fill_color, *background_border
*background_fill_color_over, *background_border_color_over, *background_gradient_over, *background_fill_color_press,
*background_border_color_press, *background_gradient_press, *background_border_width, *background_border_sides_top,
*background_border_sides_bottom, *background_border_sides_left, *background_border_sides_right,
*background_corner_radius;
*background_corner_radius, *background_border_content_tint_weight, *background_fill_content_tint_weight;
// gradients
enum { grColPixbuf = 0, grColId, grColText, grNumCols };

View File

@@ -69,23 +69,23 @@ void config_read_file(const char *path)
if (!config_has_panel_items) {
char panel_items[256];
panel_items[0] = 0;
strcat(panel_items, "T");
strlcat(panel_items, "T", sizeof(panel_items));
if (config_has_battery) {
if (config_battery_enabled)
strcat(panel_items, "B");
strlcat(panel_items, "B", sizeof(panel_items));
} else {
if (no_items_battery_enabled)
strcat(panel_items, "B");
strlcat(panel_items, "B", sizeof(panel_items));
}
if (config_has_systray) {
if (config_systray_enabled)
strcat(panel_items, "S");
strlcat(panel_items, "S", sizeof(panel_items));
} else {
if (no_items_systray_enabled)
strcat(panel_items, "S");
strlcat(panel_items, "S", sizeof(panel_items));
}
if (no_items_clock_enabled)
strcat(panel_items, "C");
strlcat(panel_items, "C", sizeof(panel_items));
set_panel_items(panel_items);
}
}
@@ -161,6 +161,8 @@ void config_write_backgrounds(FILE *fp)
int r;
int b;
double fill_weight;
double border_weight;
gboolean sideTop;
gboolean sideBottom;
gboolean sideLeft;
@@ -228,6 +230,10 @@ void config_write_backgrounds(FILE *fp)
&sideLeft,
bgColBorderSidesRight,
&sideRight,
bgColFillWeight,
&fill_weight,
bgColBorderWeight,
&border_weight,
-1);
fprintf(fp, "# Background %d: %s\n", index, text ? text : "");
fprintf(fp, "rounded = %d\n", r);
@@ -236,15 +242,18 @@ void config_write_backgrounds(FILE *fp)
char sides[10];
sides[0] = '\0';
if (sideTop)
strcat(sides, "T");
strlcat(sides, "T", sizeof(sides));
if (sideBottom)
strcat(sides, "B");
strlcat(sides, "B", sizeof(sides));
if (sideLeft)
strcat(sides, "L");
strlcat(sides, "L", sizeof(sides));
if (sideRight)
strcat(sides, "R");
strlcat(sides, "R", sizeof(sides));
fprintf(fp, "border_sides = %s\n", sides);
fprintf(fp, "border_content_tint_weight = %d\n", (int)(border_weight));
fprintf(fp, "background_content_tint_weight = %d\n", (int)(fill_weight));
config_write_color(fp, "background_color", *fillColor, fillOpacity);
config_write_color(fp, "border_color", *borderColor, borderOpacity);
if (gradient_id >= 0)
@@ -326,17 +335,15 @@ void config_write_panel(FILE *fp)
fprintf(fp, "\n");
fprintf(fp, "panel_monitor = ");
if (gtk_combo_box_get_active(GTK_COMBO_BOX(panel_combo_monitor)) == 0) {
if (gtk_combo_box_get_active(GTK_COMBO_BOX(panel_combo_monitor)) <= 0) {
fprintf(fp, "all");
} else if (gtk_combo_box_get_active(GTK_COMBO_BOX(panel_combo_monitor)) == 1) {
fprintf(fp, "primary");
} else {
fprintf(fp, "%d", gtk_combo_box_get_active(GTK_COMBO_BOX(panel_combo_monitor)));
fprintf(fp, "%d", gtk_combo_box_get_active(GTK_COMBO_BOX(panel_combo_monitor)) - 1);
}
fprintf(fp, "\n");
fprintf(fp,
"primary_monitor_first = %d\n",
gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(panel_primary_monitor_first)) ? 1 : 0);
fprintf(fp, "panel_shrink = %d\n", gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(panel_shrink)) ? 1 : 0);
fprintf(fp, "autohide = %d\n", gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(panel_autohide)) ? 1 : 0);
@@ -371,6 +378,13 @@ void config_write_panel(FILE *fp)
(int)gtk_spin_button_get_value(GTK_SPIN_BUTTON(mouse_pressed_icon_saturation)),
(int)gtk_spin_button_get_value(GTK_SPIN_BUTTON(mouse_pressed_icon_brightness)));
fprintf(fp,
"scale_relative_to_dpi = %d\n",
(int)gtk_spin_button_get_value(GTK_SPIN_BUTTON(scale_relative_to_dpi)));
fprintf(fp,
"scale_relative_to_screen_height = %d\n",
(int)gtk_spin_button_get_value(GTK_SPIN_BUTTON(scale_relative_to_screen_height)));
fprintf(fp, "\n");
}
@@ -401,6 +415,9 @@ void config_write_taskbar(FILE *fp)
fprintf(fp,
"taskbar_hide_different_monitor = %d\n",
gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(taskbar_hide_diff_monitor)) ? 1 : 0);
fprintf(fp,
"taskbar_hide_different_desktop = %d\n",
gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(taskbar_hide_diff_desktop)) ? 1 : 0);
fprintf(fp,
"taskbar_always_show_all_desktop_tasks = %d\n",
gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(taskbar_always_show_all_desktop_tasks)) ? 1 : 0);
@@ -440,10 +457,12 @@ void config_write_taskbar(FILE *fp)
} else if (gtk_combo_box_get_active(GTK_COMBO_BOX(taskbar_sort_order)) == 1) {
fprintf(fp, "title");
} else if (gtk_combo_box_get_active(GTK_COMBO_BOX(taskbar_sort_order)) == 2) {
fprintf(fp, "center");
fprintf(fp, "application");
} else if (gtk_combo_box_get_active(GTK_COMBO_BOX(taskbar_sort_order)) == 3) {
fprintf(fp, "mru");
fprintf(fp, "center");
} else if (gtk_combo_box_get_active(GTK_COMBO_BOX(taskbar_sort_order)) == 4) {
fprintf(fp, "mru");
} else if (gtk_combo_box_get_active(GTK_COMBO_BOX(taskbar_sort_order)) == 5) {
fprintf(fp, "lru");
} else {
fprintf(fp, "none");
@@ -468,7 +487,7 @@ void config_write_task_font_color(FILE *fp, char *name, GtkWidget *task_color)
GdkColor color;
gtk_color_button_get_color(GTK_COLOR_BUTTON(task_color), &color);
char full_name[128];
sprintf(full_name, "task%s_font_color", name);
snprintf(full_name, sizeof(full_name), "task%s_font_color", name);
config_write_color(fp, full_name, color, gtk_color_button_get_alpha(GTK_COLOR_BUTTON(task_color)) * 100 / 0xffff);
}
@@ -479,7 +498,7 @@ void config_write_task_icon_osb(FILE *fp,
GtkWidget *widget_brightness)
{
char full_name[128];
sprintf(full_name, "task%s_icon_asb", name);
snprintf(full_name, sizeof(full_name), "task%s_icon_asb", name);
fprintf(fp,
"%s = %d %d %d\n",
full_name,
@@ -491,7 +510,7 @@ void config_write_task_icon_osb(FILE *fp,
void config_write_task_background(FILE *fp, char *name, GtkWidget *task_background)
{
char full_name[128];
sprintf(full_name, "task%s_background_id", name);
snprintf(full_name, sizeof(full_name), "task%s_background_id", name);
fprintf(fp, "%s = %d\n", full_name, gtk_combo_box_get_active(GTK_COMBO_BOX(task_background)));
}
@@ -516,6 +535,11 @@ void config_write_task(FILE *fp)
if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(task_font_set)))
fprintf(fp, "task_font = %s\n", gtk_font_button_get_font_name(GTK_FONT_BUTTON(task_font)));
fprintf(fp, "task_tooltip = %d\n", gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(tooltip_task_show)) ? 1 : 0);
fprintf(fp, "task_thumbnail = %d\n", gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(tooltip_task_thumbnail)) ? 1 : 0);
fprintf(fp,
"task_thumbnail_size = %d\n",
(int)gtk_spin_button_get_value(GTK_SPIN_BUTTON(tooltip_task_thumbnail_size)));
// same for: "" _normal _active _urgent _iconified
if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(task_default_color_set))) {
@@ -629,7 +653,11 @@ void config_write_systray(FILE *fp)
(int)gtk_spin_button_get_value(GTK_SPIN_BUTTON(systray_icon_brightness)));
fprintf(fp, "systray_monitor = ");
fprintf(fp, "%d", MAX(1, 1 + gtk_combo_box_get_active(GTK_COMBO_BOX(systray_monitor))));
if (gtk_combo_box_get_active(GTK_COMBO_BOX(systray_monitor)) <= 0) {
fprintf(fp, "primary");
} else {
fprintf(fp, "%d", MAX(1, gtk_combo_box_get_active(GTK_COMBO_BOX(systray_monitor))));
}
fprintf(fp, "\n");
fprintf(fp, "systray_name_filter = %s\n", gtk_entry_get_text(GTK_ENTRY(systray_name_filter)));
@@ -752,6 +780,7 @@ void config_write_battery(FILE *fp)
fprintf(fp, "battery_tooltip = %d\n", gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(battery_tooltip)) ? 1 : 0);
fprintf(fp, "battery_low_status = %g\n", gtk_spin_button_get_value(GTK_SPIN_BUTTON(battery_alert_if_lower)));
fprintf(fp, "battery_low_cmd = %s\n", gtk_entry_get_text(GTK_ENTRY(battery_alert_cmd)));
fprintf(fp, "battery_full_cmd = %s\n", gtk_entry_get_text(GTK_ENTRY(battery_alert_full_cmd)));
if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(battery_font_line1_set)))
fprintf(fp, "bat1_font = %s\n", gtk_font_button_get_font_name(GTK_FONT_BUTTON(battery_font_line1)));
if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(battery_font_line2_set)))
@@ -762,6 +791,8 @@ void config_write_battery(FILE *fp)
"battery_font_color",
color,
gtk_color_button_get_alpha(GTK_COLOR_BUTTON(battery_font_color)) * 100 / 0xffff);
fprintf(fp, "bat1_format = %s\n", gtk_entry_get_text(GTK_ENTRY(battery_format1)));
fprintf(fp, "bat2_format = %s\n", gtk_entry_get_text(GTK_ENTRY(battery_format2)));
fprintf(fp,
"battery_padding = %d %d\n",
(int)gtk_spin_button_get_value(GTK_SPIN_BUTTON(battery_padding_x)),
@@ -973,7 +1004,7 @@ unsigned short checksum_txt(FILE *f)
void config_save_file(const char *path)
{
printf("config_save_file : %s\n", path);
fprintf(stderr, "tint2: config_save_file : %s\n", path);
FILE *fp;
if ((fp = fopen(path, "w+t")) == NULL)
@@ -1086,7 +1117,13 @@ void add_entry(char *key, char *value)
char *value1 = 0, *value2 = 0, *value3 = 0;
/* Gradients */
if (strcmp(key, "gradient") == 0) {
if (strcmp(key, "scale_relative_to_dpi") == 0) {
extract_values(value, &value1, &value2, &value3);
gtk_spin_button_set_value(GTK_SPIN_BUTTON(scale_relative_to_dpi), atoi(value1));
} else if (strcmp(key, "scale_relative_to_screen_height") == 0) {
extract_values(value, &value1, &value2, &value3);
gtk_spin_button_set_value(GTK_SPIN_BUTTON(scale_relative_to_screen_height), atoi(value1));
} else if (strcmp(key, "gradient") == 0) {
finalize_gradient();
GradientConfigType t;
if (g_str_equal(value, "horizontal"))
@@ -1217,6 +1254,12 @@ void add_entry(char *key, char *value)
int id = gradient_index_safe(atoi(value));
gtk_combo_box_set_active(GTK_COMBO_BOX(background_gradient_press), id);
background_force_update();
} else if (strcmp(key, "border_content_tint_weight") == 0) {
gtk_spin_button_set_value(GTK_SPIN_BUTTON(background_border_content_tint_weight), atoi(value));
background_force_update();
} else if (strcmp(key, "background_content_tint_weight") == 0) {
gtk_spin_button_set_value(GTK_SPIN_BUTTON(background_fill_content_tint_weight), atoi(value));
background_force_update();
}
/* Panel */
@@ -1351,20 +1394,20 @@ void add_entry(char *key, char *value)
} else if (strcmp(key, "panel_monitor") == 0) {
if (strcmp(value, "all") == 0)
gtk_combo_box_set_active(GTK_COMBO_BOX(panel_combo_monitor), 0);
else if (strcmp(value, "1") == 0)
else if (strcmp(value, "primary") == 0)
gtk_combo_box_set_active(GTK_COMBO_BOX(panel_combo_monitor), 1);
else if (strcmp(value, "2") == 0)
else if (strcmp(value, "1") == 0)
gtk_combo_box_set_active(GTK_COMBO_BOX(panel_combo_monitor), 2);
else if (strcmp(value, "3") == 0)
else if (strcmp(value, "2") == 0)
gtk_combo_box_set_active(GTK_COMBO_BOX(panel_combo_monitor), 3);
else if (strcmp(value, "4") == 0)
else if (strcmp(value, "3") == 0)
gtk_combo_box_set_active(GTK_COMBO_BOX(panel_combo_monitor), 4);
else if (strcmp(value, "5") == 0)
else if (strcmp(value, "4") == 0)
gtk_combo_box_set_active(GTK_COMBO_BOX(panel_combo_monitor), 5);
else if (strcmp(value, "6") == 0)
else if (strcmp(value, "5") == 0)
gtk_combo_box_set_active(GTK_COMBO_BOX(panel_combo_monitor), 6);
} else if (strcmp(key, "primary_monitor_first") == 0) {
gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(panel_primary_monitor_first), atoi(value));
else if (strcmp(value, "6") == 0)
gtk_combo_box_set_active(GTK_COMBO_BOX(panel_combo_monitor), 7);
} else if (strcmp(key, "panel_shrink") == 0) {
gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(panel_shrink), atoi(value));
}
@@ -1403,12 +1446,18 @@ void add_entry(char *key, char *value)
gtk_spin_button_set_value(GTK_SPIN_BUTTON(battery_alert_if_lower), atof(value));
} else if (strcmp(key, "battery_low_cmd") == 0) {
gtk_entry_set_text(GTK_ENTRY(battery_alert_cmd), value);
} else if (strcmp(key, "battery_full_cmd") == 0) {
gtk_entry_set_text(GTK_ENTRY(battery_alert_full_cmd), value);
} else if (strcmp(key, "bat1_font") == 0) {
gtk_font_button_set_font_name(GTK_FONT_BUTTON(battery_font_line1), value);
gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(battery_font_line1_set), TRUE);
} else if (strcmp(key, "bat2_font") == 0) {
gtk_font_button_set_font_name(GTK_FONT_BUTTON(battery_font_line2), value);
gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(battery_font_line2_set), TRUE);
} else if (strcmp(key, "bat1_format") == 0) {
gtk_entry_set_text(GTK_ENTRY(battery_format1), value);
} else if (strcmp(key, "bat2_format") == 0) {
gtk_entry_set_text(GTK_ENTRY(battery_format2), value);
} else if (strcmp(key, "battery_font_color") == 0) {
extract_values(value, &value1, &value2, &value3);
GdkColor col;
@@ -1512,12 +1561,14 @@ void add_entry(char *key, char *value)
gtk_combo_box_set_active(GTK_COMBO_BOX(taskbar_sort_order), 0);
else if (strcmp(value, "title") == 0)
gtk_combo_box_set_active(GTK_COMBO_BOX(taskbar_sort_order), 1);
else if (strcmp(value, "center") == 0)
else if (strcmp(value, "application") == 0)
gtk_combo_box_set_active(GTK_COMBO_BOX(taskbar_sort_order), 2);
else if (strcmp(value, "mru") == 0)
else if (strcmp(value, "center") == 0)
gtk_combo_box_set_active(GTK_COMBO_BOX(taskbar_sort_order), 3);
else if (strcmp(value, "lru") == 0)
else if (strcmp(value, "mru") == 0)
gtk_combo_box_set_active(GTK_COMBO_BOX(taskbar_sort_order), 4);
else if (strcmp(value, "lru") == 0)
gtk_combo_box_set_active(GTK_COMBO_BOX(taskbar_sort_order), 5);
else
gtk_combo_box_set_active(GTK_COMBO_BOX(taskbar_sort_order), 0);
} else if (strcmp(key, "task_align") == 0) {
@@ -1551,6 +1602,8 @@ void add_entry(char *key, char *value)
gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(taskbar_hide_inactive_tasks), atoi(value));
} else if (strcmp(key, "taskbar_hide_different_monitor") == 0) {
gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(taskbar_hide_diff_monitor), atoi(value));
} else if (strcmp(key, "taskbar_hide_different_desktop") == 0) {
gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(taskbar_hide_diff_desktop), atoi(value));
} else if (strcmp(key, "taskbar_always_show_all_desktop_tasks") == 0) {
gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(taskbar_always_show_all_desktop_tasks), atoi(value));
} else if (strcmp(key, "taskbar_name_padding") == 0) {
@@ -1716,6 +1769,10 @@ void add_entry(char *key, char *value)
else if (strcmp(key, "task_tooltip") == 0 || strcmp(key, "tooltip") == 0) {
gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(tooltip_task_show), atoi(value));
}
else if (strcmp(key, "task_thumbnail") == 0)
gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(tooltip_task_thumbnail), atoi(value));
else if (strcmp(key, "task_thumbnail_size") == 0)
gtk_spin_button_set_value(GTK_SPIN_BUTTON(tooltip_task_thumbnail_size), MAX(8, atoi(value)));
/* Systray */
else if (strcmp(key, "systray") == 0) {
@@ -1747,18 +1804,20 @@ void add_entry(char *key, char *value)
} else if (strcmp(key, "systray_icon_size") == 0) {
gtk_spin_button_set_value(GTK_SPIN_BUTTON(systray_icon_size), atoi(value));
} else if (strcmp(key, "systray_monitor") == 0) {
if (strcmp(value, "1") == 0)
if (strcmp(value, "primary") == 0)
gtk_combo_box_set_active(GTK_COMBO_BOX(systray_monitor), 0);
else if (strcmp(value, "2") == 0)
else if (strcmp(value, "1") == 0)
gtk_combo_box_set_active(GTK_COMBO_BOX(systray_monitor), 1);
else if (strcmp(value, "3") == 0)
else if (strcmp(value, "2") == 0)
gtk_combo_box_set_active(GTK_COMBO_BOX(systray_monitor), 2);
else if (strcmp(value, "4") == 0)
else if (strcmp(value, "3") == 0)
gtk_combo_box_set_active(GTK_COMBO_BOX(systray_monitor), 3);
else if (strcmp(value, "5") == 0)
else if (strcmp(value, "4") == 0)
gtk_combo_box_set_active(GTK_COMBO_BOX(systray_monitor), 4);
else if (strcmp(value, "6") == 0)
else if (strcmp(value, "5") == 0)
gtk_combo_box_set_active(GTK_COMBO_BOX(systray_monitor), 5);
else if (strcmp(value, "6") == 0)
gtk_combo_box_set_active(GTK_COMBO_BOX(systray_monitor), 6);
} else if (strcmp(key, "systray_icon_asb") == 0) {
extract_values(value, &value1, &value2, &value3);
gtk_spin_button_set_value(GTK_SPIN_BUTTON(systray_icon_opacity), atoi(value1));

View File

@@ -145,10 +145,16 @@ void theme_list_append(const gchar *path)
GtkTreeIter iter;
gtk_list_store_append(theme_list_store, &iter);
gchar *name = strrchr(path, '/') + 1;
gchar *name, *dir;
gchar *dir = g_strdup(path);
strrchr(dir, '/')[0] = 0;
if (strchr(path, '/')) {
name = strrchr(path, '/') + 1;
dir = g_strdup(path);
strrchr(dir, '/')[0] = 0;
} else {
name = (gchar*)path;
dir = g_strdup(".");
}
char *suffix = contract_tilde(dir);
g_free(dir);
@@ -171,7 +177,7 @@ gboolean update_snapshot(gpointer ignored)
{
gchar *tint2_cache_dir = g_build_filename(g_get_user_cache_dir(), "tint2", NULL);
if (!g_file_test(tint2_cache_dir, G_FILE_TEST_IS_DIR))
g_mkdir(tint2_cache_dir, 0700);
g_mkdir_with_parents(tint2_cache_dir, 0700);
g_free(tint2_cache_dir);
}
@@ -201,7 +207,7 @@ gboolean update_snapshot(gpointer ignored)
char hash[MD4_HEX_SIZE + 4];
md4hexf(path, hash);
strcat(hash, ".png");
strlcat(hash, ".png", sizeof(hash));
gchar *snap = g_build_filename(g_get_user_cache_dir(), "tint2", hash, NULL);
pixbuf = force_refresh ? NULL : gdk_pixbuf_new_from_file(snap, NULL);

View File

@@ -31,9 +31,9 @@ static int x, y, width, height;
static gboolean just_shown;
// the next functions are helper functions for tooltip handling
void start_show_timeout();
void start_hide_timeout();
void stop_tooltip_timeout();
void start_show_timer();
void start_hide_timer();
void stop_tooltip_timer();
void tooltip_init_fonts();
@@ -44,6 +44,9 @@ void default_tooltip()
// give the tooltip some reasonable default values
memset(&g_tooltip, 0, sizeof(Tooltip));
INIT_TIMER(g_tooltip.visibility_timer);
INIT_TIMER(g_tooltip.update_timer);
g_tooltip.font_color.rgb[0] = 1;
g_tooltip.font_color.rgb[1] = 1;
g_tooltip.font_color.rgb[2] = 1;
@@ -53,9 +56,11 @@ void default_tooltip()
void cleanup_tooltip()
{
stop_tooltip_timeout();
stop_tooltip_timer();
destroy_timer(&g_tooltip.visibility_timer);
destroy_timer(&g_tooltip.update_timer);
tooltip_hide(NULL);
tooltip_copy_text(NULL);
tooltip_update_contents_for(NULL);
if (g_tooltip.window)
XDestroyWindow(server.display, g_tooltip.window);
g_tooltip.window = 0;
@@ -118,11 +123,11 @@ void tooltip_trigger_show(Area *area, Panel *p, XEvent *e)
just_shown = TRUE;
g_tooltip.panel = p;
if (g_tooltip.mapped && g_tooltip.area != area) {
tooltip_copy_text(area);
tooltip_update_contents_for(area);
tooltip_update();
stop_tooltip_timeout();
stop_tooltip_timer();
} else if (!g_tooltip.mapped) {
start_show_timeout();
start_show_timer();
}
}
@@ -133,7 +138,7 @@ void tooltip_show(void *arg)
XTranslateCoordinates(server.display, server.root_win, g_tooltip.panel->main_win, x, y, &mx, &my, &w);
Area *area = find_area_under_mouse(g_tooltip.panel, mx, my);
if (!g_tooltip.mapped && area->_get_tooltip_text) {
tooltip_copy_text(area);
tooltip_update_contents_for(area);
g_tooltip.mapped = True;
XMapWindow(server.display, g_tooltip.window);
tooltip_update();
@@ -144,24 +149,35 @@ void tooltip_show(void *arg)
void tooltip_update_geometry()
{
Panel *panel = g_tooltip.panel;
int screen_width = server.monitors[panel->monitor].x + server.monitors[panel->monitor].width;
int screen_width = server.monitors[panel->monitor].width;
cairo_surface_t *cs = cairo_xlib_surface_create(server.display, g_tooltip.window, server.visual, width, height);
cairo_t *c = cairo_create(cs);
PangoLayout *layout = pango_cairo_create_layout(c);
PangoContext *context = pango_cairo_create_context(c);
pango_cairo_context_set_resolution(context, 96 * panel->scale);
PangoLayout *layout = pango_layout_new(context);
pango_layout_set_font_description(layout, g_tooltip.font_desc);
PangoRectangle r1, r2;
pango_layout_set_text(layout, "1234567890", -1);
pango_layout_set_text(layout, "1234567890abcdef", -1);
pango_layout_get_pixel_extents(layout, &r1, &r2);
int max_width = MIN(r2.width * 7, screen_width * 2 / 3);
int max_width = MIN(r2.width * 5, screen_width * 2 / 3);
if (g_tooltip.image && cairo_image_surface_get_width(g_tooltip.image) > 0) {
max_width = left_right_bg_border_width(g_tooltip.bg) + 2 * g_tooltip.paddingx * panel->scale +
cairo_image_surface_get_width(g_tooltip.image);
}
pango_layout_set_width(layout, max_width * PANGO_SCALE);
pango_layout_set_text(layout, g_tooltip.tooltip_text, -1);
pango_layout_set_text(layout, g_tooltip.tooltip_text ? g_tooltip.tooltip_text : "1234567890abcdef", -1);
pango_layout_set_wrap(layout, PANGO_WRAP_WORD);
pango_layout_get_pixel_extents(layout, &r1, &r2);
width = left_right_bg_border_width(g_tooltip.bg) + 2 * g_tooltip.paddingx + r2.width;
height = top_bottom_bg_border_width(g_tooltip.bg) + 2 * g_tooltip.paddingy + r2.height;
width = left_right_bg_border_width(g_tooltip.bg) + 2 * g_tooltip.paddingx * panel->scale + r2.width;
height = top_bottom_bg_border_width(g_tooltip.bg) + 2 * g_tooltip.paddingy * panel->scale + r2.height;
if (g_tooltip.image && cairo_image_surface_get_width(g_tooltip.image) > 0) {
width = left_right_bg_border_width(g_tooltip.bg) + 2 * g_tooltip.paddingx * panel->scale +
cairo_image_surface_get_width(g_tooltip.image);
height += g_tooltip.paddingy * panel->scale + cairo_image_surface_get_height(g_tooltip.image);
}
if (panel_horizontal && panel_position & BOTTOM)
y = panel->posy - height;
@@ -173,6 +189,7 @@ void tooltip_update_geometry()
x = panel->posx - width;
g_object_unref(layout);
g_object_unref(context);
cairo_destroy(c);
cairo_surface_destroy(cs);
}
@@ -229,6 +246,7 @@ void tooltip_update()
tooltip_hide(0);
return;
}
Panel *panel = g_tooltip.panel;
tooltip_update_geometry();
if (just_shown) {
@@ -263,7 +281,9 @@ void tooltip_update()
Color fc = g_tooltip.font_color;
cairo_set_source_rgba(c, fc.rgb[0], fc.rgb[1], fc.rgb[2], fc.alpha);
PangoLayout *layout = pango_cairo_create_layout(c);
PangoContext *context = pango_cairo_create_context(c);
pango_cairo_context_set_resolution(context, 96 * panel->scale);
PangoLayout *layout = pango_layout_new(context);
pango_layout_set_font_description(layout, g_tooltip.font_desc);
pango_layout_set_wrap(layout, PANGO_WRAP_WORD);
pango_layout_set_text(layout, g_tooltip.tooltip_text, -1);
@@ -275,11 +295,20 @@ void tooltip_update()
// I do not know why this is the right way, but with the below cairo_move_to it seems to be centered (horiz. and
// vert.)
cairo_move_to(c,
-r1.x / 2 + left_bg_border_width(g_tooltip.bg) + g_tooltip.paddingx,
-r1.y / 2 + 1 + top_bg_border_width(g_tooltip.bg) + g_tooltip.paddingy);
-r1.x / 2 + left_bg_border_width(g_tooltip.bg) + g_tooltip.paddingx * panel->scale,
-r1.y / 2 + 1 + top_bg_border_width(g_tooltip.bg) + g_tooltip.paddingy * panel->scale);
pango_cairo_show_layout(c, layout);
g_object_unref(layout);
g_object_unref(context);
if (g_tooltip.image) {
cairo_translate(c,
left_bg_border_width(g_tooltip.bg) + g_tooltip.paddingx * panel->scale,
height - bottom_bg_border_width(g_tooltip.bg) - g_tooltip.paddingy * panel->scale - cairo_image_surface_get_height(g_tooltip.image));
cairo_set_source_surface(c, g_tooltip.image, 0, 0);
cairo_paint(c);
}
cairo_destroy(c);
cairo_surface_destroy(cs);
}
@@ -287,11 +316,11 @@ void tooltip_update()
void tooltip_trigger_hide()
{
if (g_tooltip.mapped) {
tooltip_copy_text(0);
start_hide_timeout();
tooltip_update_contents_for(NULL);
start_hide_timer();
} else {
// tooltip not visible yet, but maybe a timeout is still pending
stop_tooltip_timeout();
// tooltip not visible yet, but maybe a timer is still pending
stop_tooltip_timer();
}
}
@@ -302,29 +331,43 @@ void tooltip_hide(void *arg)
XUnmapWindow(server.display, g_tooltip.window);
XFlush(server.display);
}
g_tooltip.area = NULL;
}
void start_show_timeout()
void start_show_timer()
{
change_timeout(&g_tooltip.timeout, g_tooltip.show_timeout_msec, 0, tooltip_show, 0);
change_timer(&g_tooltip.visibility_timer, true, g_tooltip.show_timeout_msec, 0, tooltip_show, 0);
}
void start_hide_timeout()
void start_hide_timer()
{
change_timeout(&g_tooltip.timeout, g_tooltip.hide_timeout_msec, 0, tooltip_hide, 0);
change_timer(&g_tooltip.visibility_timer, true, g_tooltip.hide_timeout_msec, 0, tooltip_hide, 0);
}
void stop_tooltip_timeout()
void stop_tooltip_timer()
{
stop_timeout(g_tooltip.timeout);
stop_timer(&g_tooltip.visibility_timer);
}
void tooltip_copy_text(Area *area)
void tooltip_update_contents_timeout(void *arg)
{
free(g_tooltip.tooltip_text);
tooltip_update_contents_for(g_tooltip.area);
}
void tooltip_update_contents_for(Area *area)
{
free_and_null(g_tooltip.tooltip_text);
if (g_tooltip.image)
cairo_surface_destroy(g_tooltip.image);
g_tooltip.image = NULL;
if (area && area->_get_tooltip_text)
g_tooltip.tooltip_text = area->_get_tooltip_text(area);
else
g_tooltip.tooltip_text = NULL;
if (area && area->_get_tooltip_image) {
g_tooltip.image = area->_get_tooltip_image(area);
if (g_tooltip.image)
cairo_surface_reference(g_tooltip.image);
else
change_timer(&g_tooltip.update_timer, true, 300, 0, tooltip_update_contents_timeout, NULL);
}
g_tooltip.area = area;
}

View File

@@ -36,7 +36,9 @@ typedef struct {
PangoFontDescription *font_desc;
Color font_color;
Background *bg;
timeout *timeout;
Timer visibility_timer;
Timer update_timer;
cairo_surface_t *image;
} Tooltip;
extern Tooltip g_tooltip;
@@ -53,7 +55,7 @@ void tooltip_show(void * /*arg*/);
void tooltip_update();
void tooltip_trigger_hide();
void tooltip_hide(void * /*arg*/);
void tooltip_copy_text(Area *area);
void tooltip_update_contents_for(Area *area);
void tooltip_default_font_changed();
#endif // TOOLTIP_H

View File

@@ -224,7 +224,7 @@ int compute_desired_size(Area *a)
if (a->_compute_desired_size)
return a->_compute_desired_size(a);
if (a->size_mode == LAYOUT_FIXED)
fprintf(stderr, YELLOW "Area %s does not set desired size!" RESET "\n", a->name);
fprintf(stderr, YELLOW "tint2: Area %s does not set desired size!" RESET "\n", a->name);
return container_compute_desired_size(a);
}
@@ -385,7 +385,7 @@ void draw_tree(Area *a)
a->posx,
a->posy);
else
fprintf(stderr, RED "%s %d: area %s has no pixmap!!!" RESET "\n", __FILE__, __LINE__, a->name);
fprintf(stderr, RED "tint2: %s %d: area %s has no pixmap!!!" RESET "\n", __FILE__, __LINE__, a->name);
for (GList *l = a->children; l; l = l->next)
draw_tree((Area *)l->data);
@@ -490,28 +490,59 @@ void draw(Area *a)
cairo_surface_destroy(cs);
}
double tint_color_channel(double a, double b, double tint_weight)
{
double gamma = 2.2;
if (tint_weight == 0.0)
return a;
double result = sqrt((1.-tint_weight)*pow(a, gamma) + tint_weight * pow(b, gamma));
return result;
}
void set_cairo_source_tinted(cairo_t *c, Color *color1, Color *color2, double tint_weight)
{
cairo_set_source_rgba(c,
tint_color_channel(color1->rgb[0], color2->rgb[0], tint_weight),
tint_color_channel(color1->rgb[1], color2->rgb[1], tint_weight),
tint_color_channel(color1->rgb[2], color2->rgb[2], tint_weight),
color1->alpha);
}
void set_cairo_source_bg_color(Area *a, cairo_t *c)
{
Color content_color;
if (a->_get_content_color)
a->_get_content_color(a, &content_color);
else
bzero(&content_color, sizeof(content_color));
if (a->mouse_state == MOUSE_OVER)
set_cairo_source_tinted(c, &a->bg->fill_color_hover, &content_color, a->bg->fill_content_tint_weight);
else if (a->mouse_state == MOUSE_DOWN)
set_cairo_source_tinted(c, &a->bg->fill_color_pressed, &content_color, a->bg->fill_content_tint_weight);
else
set_cairo_source_tinted(c, &a->bg->fill_color, &content_color, a->bg->fill_content_tint_weight);
}
void set_cairo_source_border_color(Area *a, cairo_t *c)
{
Color content_color;
if (a->_get_content_color)
a->_get_content_color(a, &content_color);
else
bzero(&content_color, sizeof(content_color));
if (a->mouse_state == MOUSE_OVER)
set_cairo_source_tinted(c, &a->bg->border_color_hover, &content_color, a->bg->border_content_tint_weight);
else if (a->mouse_state == MOUSE_DOWN)
set_cairo_source_tinted(c, &a->bg->border_color_pressed, &content_color, a->bg->border_content_tint_weight);
else
set_cairo_source_tinted(c, &a->bg->border.color, &content_color, a->bg->border_content_tint_weight);
}
void draw_background(Area *a, cairo_t *c)
{
if ((a->bg->fill_color.alpha > 0.0) ||
(panel_config.mouse_effects && (a->has_mouse_over_effect || a->has_mouse_press_effect))) {
if (a->mouse_state == MOUSE_OVER)
cairo_set_source_rgba(c,
a->bg->fill_color_hover.rgb[0],
a->bg->fill_color_hover.rgb[1],
a->bg->fill_color_hover.rgb[2],
a->bg->fill_color_hover.alpha);
else if (a->mouse_state == MOUSE_DOWN)
cairo_set_source_rgba(c,
a->bg->fill_color_pressed.rgb[0],
a->bg->fill_color_pressed.rgb[1],
a->bg->fill_color_pressed.rgb[2],
a->bg->fill_color_pressed.alpha);
else
cairo_set_source_rgba(c,
a->bg->fill_color.rgb[0],
a->bg->fill_color.rgb[1],
a->bg->fill_color.rgb[2],
a->bg->fill_color.alpha);
// Not sure about this
draw_rect(c,
left_border_width(a),
@@ -519,7 +550,7 @@ void draw_background(Area *a, cairo_t *c)
a->width - left_right_border_width(a),
a->height - top_bottom_border_width(a),
a->bg->border.radius - a->bg->border.width / 1.571);
set_cairo_source_bg_color(a, c);
cairo_fill(c);
}
for (GList *l = a->gradient_instances_by_state[a->mouse_state]; l; l = l->next) {
@@ -540,24 +571,7 @@ void draw_background(Area *a, cairo_t *c)
cairo_set_line_width(c, a->bg->border.width);
// draw border inside (x, y, width, height)
if (a->mouse_state == MOUSE_OVER)
cairo_set_source_rgba(c,
a->bg->border_color_hover.rgb[0],
a->bg->border_color_hover.rgb[1],
a->bg->border_color_hover.rgb[2],
a->bg->border_color_hover.alpha);
else if (a->mouse_state == MOUSE_DOWN)
cairo_set_source_rgba(c,
a->bg->border_color_pressed.rgb[0],
a->bg->border_color_pressed.rgb[1],
a->bg->border_color_pressed.rgb[2],
a->bg->border_color_pressed.alpha);
else
cairo_set_source_rgba(c,
a->bg->border.color.rgb[0],
a->bg->border.color.rgb[1],
a->bg->border.color.rgb[2],
a->bg->border.color.alpha);
set_cairo_source_border_color(a, c);
draw_rect_on_sides(c,
left_border_width(a) / 2.,
top_border_width(a) / 2.,
@@ -630,7 +644,7 @@ void free_area(Area *a)
free_area_gradient_instances(a);
}
void mouse_over(Area *area, int pressed)
void mouse_over(Area *area, gboolean pressed)
{
if (mouse_over_area == area && !area)
return;
@@ -843,14 +857,14 @@ int top_bottom_bg_border_width(Background *bg)
void area_dump_geometry(Area *area, int indent)
{
fprintf(stderr, "%*s%s:\n", indent, "", area->name);
fprintf(stderr, "tint2: %*s%s:\n", indent, "", area->name);
indent += 2;
if (!area->on_screen) {
fprintf(stderr, "%*shidden\n", indent, "");
fprintf(stderr, "tint2: %*shidden\n", indent, "");
return;
}
fprintf(stderr,
"%*sBox: x = %d, y = %d, w = %d, h = %d, desired size = %d\n",
"tint2: %*sBox: x = %d, y = %d, w = %d, h = %d, desired size = %d\n",
indent,
"",
area->posx,
@@ -859,7 +873,7 @@ void area_dump_geometry(Area *area, int indent)
area->height,
compute_desired_size(area));
fprintf(stderr,
"%*sBorder: left = %d, right = %d, top = %d, bottom = %d\n",
"tint2: %*sBorder: left = %d, right = %d, top = %d, bottom = %d\n",
indent,
"",
left_border_width(area),
@@ -867,7 +881,7 @@ void area_dump_geometry(Area *area, int indent)
top_border_width(area),
bottom_border_width(area));
fprintf(stderr,
"%*sPadding: left = right = %d, top = bottom = %d, spacing = %d\n",
"tint2: %*sPadding: left = right = %d, top = bottom = %d, spacing = %d\n",
indent,
"",
area->paddingxlr,
@@ -876,13 +890,215 @@ void area_dump_geometry(Area *area, int indent)
if (area->_dump_geometry)
area->_dump_geometry(area, indent);
if (area->children) {
fprintf(stderr, "%*sChildren:\n", indent, "");
fprintf(stderr, "tint2: %*sChildren:\n", indent, "");
indent += 2;
for (GList *l = area->children; l; l = l->next)
area_dump_geometry((Area *)l->data, indent);
}
}
void area_compute_available_size(Area *area,
int *available_w,
int *available_h)
{
Panel *panel = (Panel *)area->panel;
if (panel_horizontal) {
*available_w = panel->area.width;
*available_h = area->height - 2 * area->paddingy - left_right_border_width(area);
} else {
*available_w = area->width - 2 * area->paddingxlr - left_right_border_width(area);
*available_h = panel->area.height;
}
}
void area_compute_inner_size(Area *area,
int *inner_w,
int *inner_h)
{
if (panel_horizontal) {
*inner_w = area->width - 2 * area->paddingxlr - left_right_border_width(area);
*inner_h = area->height - 2 * area->paddingy - top_bottom_border_width(area);
} else {
*inner_w = area->width - 2 * area->paddingxlr - left_right_border_width(area);
*inner_h = area->height - 2 * area->paddingy - top_bottom_border_width(area);
}
}
void area_compute_text_geometry(Area *area,
const char *line1,
const char *line2,
PangoFontDescription *line1_font_desc,
PangoFontDescription *line2_font_desc,
int *line1_height,
int *line1_width,
int *line2_height,
int *line2_width)
{
int available_w, available_h;
area_compute_available_size(area, &available_w, &available_h);
if (line1 && line1[0])
get_text_size2(line1_font_desc,
line1_height,
line1_width,
available_h,
available_w,
line1,
strlen(line1),
PANGO_WRAP_WORD_CHAR,
PANGO_ELLIPSIZE_NONE,
PANGO_ALIGN_CENTER,
FALSE,
((Panel*)area->panel)->scale);
else
*line1_width = *line1_height = 0;
if (line2 && line2[0])
get_text_size2(line2_font_desc,
line2_height,
line2_width,
available_h,
available_w,
line2,
strlen(line2),
PANGO_WRAP_WORD_CHAR,
PANGO_ELLIPSIZE_NONE,
PANGO_ALIGN_CENTER,
FALSE,
((Panel*)area->panel)->scale);
else
*line2_width = *line2_height = 0;
}
int text_area_compute_desired_size(Area *area,
const char *line1,
const char *line2,
PangoFontDescription *line1_font_desc,
PangoFontDescription *line2_font_desc)
{
int line1_height, line1_width, line2_height, line2_width;
area_compute_text_geometry(area,
line1,
line2,
line1_font_desc,
line2_font_desc,
&line1_height,
&line1_width,
&line2_height,
&line2_width);
if (panel_horizontal) {
int new_size = MAX(line1_width, line2_width) + 2 * area->paddingxlr + left_right_border_width(area);
return new_size;
} else {
int new_size = line1_height + line2_height + 2 * area->paddingy + top_bottom_border_width(area);
return new_size;
}
}
gboolean resize_text_area(Area *area,
const char *line1,
const char *line2,
PangoFontDescription *line1_font_desc,
PangoFontDescription *line2_font_desc,
int *line1_posy,
int *line2_posy)
{
gboolean result = FALSE;
schedule_redraw(area);
int line1_height, line1_width;
int line2_height, line2_width;
area_compute_text_geometry(area,
line1,
line2,
line1_font_desc,
line2_font_desc,
&line1_height,
&line1_width,
&line2_height,
&line2_width);
int new_size = text_area_compute_desired_size(area,
line1,
line2,
line1_font_desc,
line2_font_desc);
if (panel_horizontal) {
if (new_size != area->width) {
if (new_size < area->width && abs(new_size - area->width) < 6) {
// we try to limit the number of resizes
new_size = area->width;
} else {
area->width = new_size;
}
*line1_posy = (area->height - line1_height) / 2;
if (line2) {
*line1_posy -= (line2_height) / 2;
*line2_posy = *line1_posy + line1_height;
}
result = TRUE;
}
} else {
if (new_size != area->height) {
area->height = new_size;
*line1_posy = (area->height - line1_height) / 2;
if (line2) {
*line1_posy -= (line2_height) / 2;
*line2_posy = *line1_posy + line1_height;
}
result = TRUE;
}
}
return result;
}
void draw_text_area(Area *area,
cairo_t *c,
const char *line1,
const char *line2,
PangoFontDescription *line1_font_desc,
PangoFontDescription *line2_font_desc,
int line1_posy,
int line2_posy,
Color *color,
double scale)
{
int inner_w, inner_h;
area_compute_inner_size(area, &inner_w, &inner_h);
PangoContext *context = pango_cairo_create_context(c);
pango_cairo_context_set_resolution(context, 96 * scale);
PangoLayout *layout = pango_layout_new(context);
pango_layout_set_alignment(layout, PANGO_ALIGN_CENTER);
pango_layout_set_wrap(layout, PANGO_WRAP_WORD_CHAR);
pango_layout_set_ellipsize(layout, PANGO_ELLIPSIZE_NONE);
pango_layout_set_width(layout, inner_w * PANGO_SCALE);
pango_layout_set_height(layout, inner_h * PANGO_SCALE);
cairo_set_source_rgba(c, color->rgb[0], color->rgb[1], color->rgb[2], color->alpha);
if (line1 && line1[0]) {
pango_layout_set_font_description(layout, line1_font_desc);
pango_layout_set_text(layout, line1, strlen(line1));
pango_cairo_update_layout(c, layout);
draw_text(layout, c, (area->width - inner_w) / 2, line1_posy, color, ((Panel *)area->panel)->font_shadow);
}
if (line2 && line2[0]) {
pango_layout_set_font_description(layout, line2_font_desc);
pango_layout_set_indent(layout, 0);
pango_layout_set_text(layout, line2, strlen(line2));
pango_cairo_update_layout(c, layout);
draw_text(layout, c, (area->width - inner_w) / 2, line2_posy, color, ((Panel *)area->panel)->font_shadow);
}
g_object_unref(layout);
g_object_unref(context);
}
Area *compute_element_area(Area *area, Element element)
{
if (element == ELEMENT_SELF)
@@ -955,7 +1171,7 @@ void free_gradient_instance(GradientInstance *gi)
void instantiate_area_gradients(Area *area)
{
if (debug_gradients)
fprintf(stderr, "Initializing gradients for area %s\n", area->name);
fprintf(stderr, "tint2: Initializing gradients for area %s\n", area->name);
for (int i = 0; i < MOUSE_STATE_COUNT; i++) {
g_assert_null(area->gradient_instances_by_state[i]);
GradientClass *g = area->bg->gradients[i];
@@ -970,7 +1186,7 @@ void instantiate_area_gradients(Area *area)
void free_area_gradient_instances(Area *area)
{
if (debug_gradients)
fprintf(stderr, "Freeing gradients for area %s\n", area->name);
fprintf(stderr, "tint2: Freeing gradients for area %s\n", area->name);
for (int i = 0; i < MOUSE_STATE_COUNT; i++) {
for (GList *l = area->gradient_instances_by_state[i]; l; l = l->next) {
GradientInstance *gi = (GradientInstance *)l->data;
@@ -996,7 +1212,7 @@ double compute_control_point_offset(Area *area, Offset *offset)
double height = element_area->height;
double radius = sqrt(element_area->width * element_area->width + element_area->height * element_area->height) / 2.0;
double left, top;
double left = 0, top = 0;
if (offset->element == ELEMENT_SELF) {
left = 0;
top = 0;

View File

@@ -154,6 +154,8 @@ typedef struct Background {
Color border_color_pressed;
// Pointer to a GradientClass or NULL, no ownership
GradientClass *gradients[MOUSE_STATE_COUNT];
double fill_content_tint_weight;
double border_content_tint_weight;
} Background;
typedef enum Layout {
@@ -233,6 +235,7 @@ typedef struct Area {
// Returns a copy of the tooltip to be displayed for this widget.
// The caller takes ownership of the pointer.
char *(*_get_tooltip_text)(void *obj);
cairo_surface_t *(*_get_tooltip_image)(void *obj);
// Returns true if the Area handles a mouse event at the given x, y coordinates relative to the window.
// Leave this to NULL to use a default implementation.
@@ -240,6 +243,8 @@ typedef struct Area {
// Prints the geometry of the object on stderr, with left indentation of indent spaces.
void (*_dump_geometry)(void *obj, int indent);
void (*_get_content_color)(void *obj, Color *color);
} Area;
// Initializes the Background member to default values.
@@ -263,6 +268,38 @@ int relayout_with_constraint(Area *a, int maximum_size);
int compute_desired_size(Area *a);
int container_compute_desired_size(Area *a);
void area_compute_text_geometry(Area *area,
const char *line1,
const char *line2,
PangoFontDescription *line1_font_desc,
PangoFontDescription *line2_font_desc,
int *line1_height,
int *line1_width,
int *line2_height,
int *line2_width);
int text_area_compute_desired_size(Area *area,
const char *line1,
const char *line2,
PangoFontDescription *line1_font_desc,
PangoFontDescription *line2_font_desc);
gboolean resize_text_area(Area *area,
const char *line1,
const char *line2,
PangoFontDescription *line1_font_desc,
PangoFontDescription *line2_font_desc,
int *line1_posy,
int *line2_posy);
void draw_text_area(Area *area,
cairo_t *c,
const char *line1,
const char *line2,
PangoFontDescription *line1_font_desc,
PangoFontDescription *line2_font_desc,
int line1_posy,
int line2_posy,
Color *color,
double scale);
int left_border_width(Area *a);
int right_border_width(Area *a);
int left_right_border_width(Area *a);
@@ -324,7 +361,7 @@ void free_area_gradient_instances(Area *area);
void area_dump_geometry(Area *area, int indent);
void mouse_over(Area *area, int pressed);
void mouse_over(Area *area, gboolean pressed);
void mouse_out();
void update_gradient(GradientInstance *gi);

17
src/util/bool.h Normal file
View File

@@ -0,0 +1,17 @@
#ifndef BOOL_H
#define BOOL_H
#ifndef bool
#define bool int
#define false 0
#define true 1
#endif
#define SUCCESS true
#define FAILURE false
#ifndef Status
typedef int Status;
#endif
#endif

268
src/util/bt.c Normal file
View File

@@ -0,0 +1,268 @@
/**************************************************************************
*
* Tint2 : backtrace
*
* Copyright (C) 2007 Pål Staurland (staura@gmail.com)
* Modified (C) 2008 thierry lorthiois (lorthiois@bbsoft.fr) from Omega distribution
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License version 2
* as published by the Free Software Foundation.
*
* This program 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 General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
**************************************************************************/
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <unistd.h>
#include <stdio.h>
#include "bt.h"
#include "bool.h"
#if defined(HAS_BACKTRACE) || defined(HAS_LIBUNWIND) || defined(HAS_EXECINFO)
static void bt_add_frame(struct backtrace *bt, const char *fname)
{
if (bt->frame_count >= BT_MAX_FRAMES)
return;
struct backtrace_frame *frame = &bt->frames[bt->frame_count];
if (fname && *fname) {
strncpy(frame->name, fname, BT_FRAME_SIZE);
} else {
strncpy(frame->name, "??", BT_FRAME_SIZE);
}
bt->frame_count++;
}
#endif
#ifdef HAS_BACKTRACE
#include <backtrace.h>
static const char *get_exe()
{
static char buf[256] = {0};
ssize_t ret = readlink("/proc/self/exe", buf, sizeof(buf)-1);
if (ret > 0)
return buf;
ret = readlink("/proc/curproc/file", buf, sizeof(buf)-1);
if (ret > 0)
return buf;
return buf;
}
static void bt_error_callback(void *data, const char *msg, int errnum)
{
}
static int bt_full_callback(void *data, uintptr_t pc,
const char *filename, int lineno,
const char *function)
{
struct backtrace *bt = (struct backtrace *)data;
bt_add_frame(bt, function);
return 0;
}
void get_backtrace(struct backtrace *bt, int skip)
{
static struct backtrace_state *state = NULL;
if (!state) {
const char *exe = get_exe();
if (exe)
state = backtrace_create_state(exe, 1, bt_error_callback, NULL);
}
bzero(bt, sizeof(*bt));
if (state) {
backtrace_full(state, skip + 1, bt_full_callback, bt_error_callback, bt);
}
}
#elif HAS_LIBUNWIND
#define UNW_LOCAL_ONLY
#include <libunwind.h>
struct bt_mapping {
unw_word_t ip;
char fname[BT_FRAME_SIZE];
};
#define BT_BUCKET_SIZE 3
struct bt_bucket {
struct bt_mapping mappings[BT_BUCKET_SIZE];
};
struct bt_cache {
struct bt_bucket *buckets;
size_t count;
size_t size;
};
static unsigned oat_hash(void *key, int len)
{
unsigned char *p = key;
unsigned h = 0;
int i;
for (i = 0; i < len; i++)
{
h += p[i];
h += (h << 10);
h ^= (h >> 6);
}
h += (h << 3);
h ^= (h >> 11);
h += (h << 15);
return h;
}
static struct bt_bucket *cached_proc_name_bucket(struct bt_cache *cache, unw_word_t ip)
{
if (!cache->size)
return NULL;
unsigned h = oat_hash(&ip, sizeof(ip));
return &cache->buckets[h % cache->size];
}
static void bt_cache_init(struct bt_cache *cache)
{
if (!cache->size) {
cache->size = 119;
cache->buckets = calloc(cache->size, sizeof(*cache->buckets));
if (!cache->buckets)
cache->size = 0;
cache->count = 0;
}
}
static void cached_proc_name_store(struct bt_cache *cache, unw_word_t ip, const char *fname);
static void bt_cache_rebalance(struct bt_cache *cache)
{
struct bt_cache bigger = {};
bigger.size = cache->size * 2 + 1337;
bigger.buckets = calloc(bigger.size, sizeof(*bigger.buckets));
bigger.count = 0;
for (size_t b = 0; b < cache->size; b++) {
for (size_t i = 0; i < BT_BUCKET_SIZE; i++) {
struct bt_mapping *map = &cache->buckets[b].mappings[i];
if (map->ip) {
cached_proc_name_store(&bigger, map->ip, map->fname);
}
}
}
free(cache->buckets);
*cache = bigger;
}
static void cached_proc_name_store(struct bt_cache *cache, unw_word_t ip, const char *fname)
{
bt_cache_init(cache);
if (!cache->size)
return;
struct bt_bucket *bucket = cached_proc_name_bucket(cache, ip);
bool stored = false;
for (size_t i = 0; i < BT_BUCKET_SIZE; i++) {
if (bucket->mappings[i].ip == ip)
return;
if (bucket->mappings[i].ip != 0)
continue;
bucket->mappings[i].ip = ip;
strncpy(bucket->mappings[i].fname, fname, sizeof(bucket->mappings[i].fname));
cache->count++;
stored = true;
break;
}
if (cache->count > cache->size / 4 || !stored) {
bt_cache_rebalance(cache);
fprintf(stderr, "tint2: proc_name cache: ratio %f, count %lu, size %lu, (%lu bytes)\n",
cache->count / (double)cache->size,
cache->count, (size_t)cache->size, cache->size * sizeof(*cache->buckets));
}
}
const char *cached_proc_name_get(struct bt_cache *cache, unw_word_t ip)
{
struct bt_bucket *bucket = cached_proc_name_bucket(cache, ip);
if (!bucket)
return NULL;
for (size_t i = 0; i < BT_BUCKET_SIZE; i++) {
if (bucket->mappings[i].ip != ip)
continue;
return bucket->mappings[i].fname;
}
return NULL;
}
void get_backtrace(struct backtrace *bt, int skip)
{
static struct bt_cache bt_cache = {};
bzero(bt, sizeof(*bt));
unw_cursor_t cursor;
unw_context_t context;
unw_getcontext(&context);
unw_init_local(&cursor, &context);
while (unw_step(&cursor) > 0) {
if (skip > 0) {
skip--;
continue;
}
unw_word_t offset;
char fname[BT_FRAME_SIZE] = {0};
unw_word_t ip;
unw_get_reg(&cursor, UNW_REG_IP, &ip);
const char *fname_cached = cached_proc_name_get(&bt_cache, ip);
if (fname_cached) {
bt_add_frame(bt, fname_cached);
} else {
unw_get_proc_name(&cursor, fname, sizeof(fname), &offset);
cached_proc_name_store(&bt_cache, ip, fname);
bt_add_frame(bt, fname);
}
}
}
#elif HAS_EXECINFO
#include <execinfo.h>
void get_backtrace(struct backtrace *bt, int skip)
{
bzero(bt, sizeof(*bt));
void *array[BT_MAX_FRAMES];
int size = backtrace(array, BT_MAX_FRAMES);
char **strings = backtrace_symbols(array, size);
for (int i = 0; i < size; i++) {
bt_add_frame(bt, strings[i]);
}
free(strings);
}
#else
void get_backtrace(struct backtrace *bt, int skip)
{
bzero(bt, sizeof(*bt));
}
#endif

20
src/util/bt.h Normal file
View File

@@ -0,0 +1,20 @@
#ifndef BT_H
#define BT_H
#include <sys/types.h>
#define BT_FRAME_SIZE 64
#define BT_MAX_FRAMES 64
struct backtrace_frame {
char name[BT_FRAME_SIZE];
};
struct backtrace {
struct backtrace_frame frames[BT_MAX_FRAMES];
size_t frame_count;
};
void get_backtrace(struct backtrace *bt, int skip);
#endif // BT_H

View File

@@ -117,14 +117,14 @@ void save_cache(Cache *cache, const gchar *cache_path)
fd = open(cache_path, O_RDONLY | O_CREAT, 0600);
}
if (fd == -1) {
fprintf(stderr, RED "Could not save icon theme cache!" RESET "\n");
fprintf(stderr, RED "tint2: Could not save icon theme cache!" RESET "\n");
return;
}
flock(fd, LOCK_EX);
FILE *f = fopen(cache_path, "w");
if (!f) {
fprintf(stderr, RED "Could not save icon theme cache!" RESET "\n");
fprintf(stderr, RED "tint2: Could not save icon theme cache!" RESET "\n");
goto unlock;
}
g_hash_table_foreach(cache->_table, write_cache_line, f);

10
src/util/colors.h Normal file
View File

@@ -0,0 +1,10 @@
#ifndef COLORS_H
#define COLORS_H
#define GREEN "\033[1;32m"
#define YELLOW "\033[1;33m"
#define RED "\033[1;31m"
#define BLUE "\033[1;34m"
#define RESET "\033[0m"
#endif

View File

@@ -31,20 +31,146 @@
#include <glib.h>
#include <glib/gstdio.h>
#include "common.h"
#include "../server.h"
#include "server.h"
#include <sys/wait.h>
#include <sys/types.h>
#include <pwd.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/time.h>
#include <errno.h>
#include <dirent.h>
#if !defined(__OpenBSD__)
#include <wordexp.h>
#endif
#ifdef HAVE_RSVG
#include <librsvg/rsvg.h>
#endif
#include "../panel.h"
#include "timer.h"
#include "signals.h"
#include "bt.h"
void write_string(int fd, const char *s)
{
int len = strlen(s);
while (len > 0) {
int count = write(fd, s, len);
if (count >= 0) {
s += count;
len -= count;
} else {
break;
}
}
}
void log_string(int fd, const char *s)
{
write_string(2, s);
write_string(fd, s);
}
void dump_backtrace(int log_fd)
{
struct backtrace bt;
get_backtrace(&bt, 1);
log_string(log_fd, "\n" YELLOW "Backtrace:" RESET "\n");
for (size_t i = 0; i < bt.frame_count; i++) {
log_string(log_fd, bt.frames[i].name);
log_string(log_fd, "\n");
}
}
// sleep() returns early when signals arrive. This function does not.
void safe_sleep(int seconds)
{
double t0 = get_time();
while (1) {
double t = get_time();
if (t > t0 + seconds)
return;
sleep(1);
}
}
const char *signal_name(int sig)
{
switch (sig) {
case SIGHUP:
return "SIGHUP: Hangup (POSIX).";
case SIGINT:
return "SIGINT: Interrupt (ANSI).";
case SIGQUIT:
return "SIGQUIT: Quit (POSIX).";
case SIGILL:
return "SIGILL: Illegal instruction (ANSI).";
case SIGTRAP:
return "SIGTRAP: Trace trap (POSIX).";
case SIGABRT:
return "SIGABRT/SIGIOT: Abort (ANSI) / IOT trap (4.2 BSD).";
case SIGBUS:
return "SIGBUS: BUS error (4.2 BSD).";
case SIGFPE:
return "SIGFPE: Floating-point exception (ANSI).";
case SIGKILL:
return "SIGKILL: Kill, unblockable (POSIX).";
case SIGUSR1:
return "SIGUSR1: User-defined signal 1 (POSIX).";
case SIGSEGV:
return "SIGSEGV: Segmentation violation (ANSI).";
case SIGUSR2:
return "SIGUSR2: User-defined signal 2 (POSIX).";
case SIGPIPE:
return "SIGPIPE: Broken pipe (POSIX).";
case SIGALRM:
return "SIGALRM: Alarm clock (POSIX).";
case SIGTERM:
return "SIGTERM: Termination (ANSI).";
// case SIGSTKFLT: return "SIGSTKFLT: Stack fault.";
case SIGCHLD:
return "SIGCHLD: Child status has changed (POSIX).";
case SIGCONT:
return "SIGCONT: Continue (POSIX).";
case SIGSTOP:
return "SIGSTOP: Stop, unblockable (POSIX).";
case SIGTSTP:
return "SIGTSTP: Keyboard stop (POSIX).";
case SIGTTIN:
return "SIGTTIN: Background read from tty (POSIX).";
case SIGTTOU:
return "SIGTTOU: Background write to tty (POSIX).";
case SIGURG:
return "SIGURG: Urgent condition on socket (4.2 BSD).";
case SIGXCPU:
return "SIGXCPU: CPU limit exceeded (4.2 BSD).";
case SIGXFSZ:
return "SIGXFSZ: File size limit exceeded (4.2 BSD).";
case SIGVTALRM:
return "SIGVTALRM: Virtual alarm clock (4.2 BSD).";
case SIGPROF:
return "SIGPROF: Profiling alarm clock (4.2 BSD).";
// case SIGPWR: return "SIGPWR: Power failure restart (System V).";
case SIGSYS:
return "SIGSYS: Bad system call.";
}
static char s[64];
snprintf(s, sizeof(s), "SIG=%d: Unknown", sig);
return s;
}
const char *get_home_dir()
{
const char *s = getenv("HOME");
if (s)
return s;
struct passwd *pw = getpwuid(getuid());
if (!pw)
return NULL;
return pw->pw_dir;
}
void copy_file(const char *path_src, const char *path_dest)
{
@@ -67,7 +193,7 @@ void copy_file(const char *path_src, const char *path_dest)
while ((nb = fread(buffer, 1, sizeof(buffer), file_src)) > 0) {
if (nb != fwrite(buffer, 1, nb, file_dest)) {
printf("Error while copying file %s to %s\n", path_src, path_dest);
fprintf(stderr, "tint2: Error while copying file %s to %s\n", path_src, path_dest);
}
}
@@ -103,24 +229,118 @@ gboolean parse_line(const char *line, char **key, char **value)
extern char *config_path;
void tint_exec(const char *command, const char *dir, const char *tooltip, Time time)
int setenvd(const char *name, const int value)
{
char buf[256];
snprintf(buf, sizeof(buf), "%d", value);
return setenv(name, buf, 1);
}
#ifndef TINT2CONF
pid_t tint_exec(const char *command,
const char *dir,
const char *tooltip,
Time time,
Area *area,
int x,
int y,
gboolean terminal,
gboolean startup_notification)
{
if (!command || strlen(command) == 0)
return;
return -1;
if (area) {
Panel *panel = (Panel *)area->panel;
int aligned_x, aligned_y, aligned_x1, aligned_y1, aligned_x2, aligned_y2;
int panel_x1, panel_x2, panel_y1, panel_y2;
if (panel_horizontal) {
if (area_is_first(area))
aligned_x1 = panel->posx;
else
aligned_x1 = panel->posx + area->posx;
if (area_is_last(area))
aligned_x2 = panel->posx + panel->area.width;
else
aligned_x2 = panel->posx + area->posx + area->width;
if (area_is_first(area))
aligned_x = aligned_x1;
else if (area_is_last(area))
aligned_x = aligned_x2;
else
aligned_x = aligned_x1;
if (panel_position & BOTTOM)
aligned_y = panel->posy;
else
aligned_y = panel->posy + panel->area.height;
aligned_y1 = aligned_y2 = aligned_y;
panel_x1 = panel->posx;
panel_x2 = panel->posx + panel->area.width;
panel_y1 = panel_y2 = aligned_y;
} else {
if (area_is_first(area))
aligned_y1 = panel->posy;
else
aligned_y1 = panel->posy + area->posy;
if (area_is_last(area))
aligned_y2 = panel->posy + panel->area.height;
else
aligned_y2 = panel->posy + area->posy + area->height;
if (area_is_first(area))
aligned_y = aligned_y1;
else if (area_is_last(area))
aligned_y = aligned_y2;
else
aligned_y = aligned_y1;
if (panel_position & RIGHT)
aligned_x = panel->posx;
else
aligned_x = panel->posx + panel->area.width;
aligned_x1 = aligned_x2 = aligned_x;
panel_x1 = panel_x2 = aligned_x;
panel_y1 = panel->posy;
panel_y2 = panel->posy + panel->area.height;
}
setenv("TINT2_CONFIG", config_path, 1);
setenvd("TINT2_BUTTON_X", x);
setenvd("TINT2_BUTTON_Y", y);
setenvd("TINT2_BUTTON_W", area->width);
setenvd("TINT2_BUTTON_H", area->height);
setenvd("TINT2_BUTTON_ALIGNED_X", aligned_x);
setenvd("TINT2_BUTTON_ALIGNED_Y", aligned_y);
setenvd("TINT2_BUTTON_ALIGNED_X1", aligned_x1);
setenvd("TINT2_BUTTON_ALIGNED_Y1", aligned_y1);
setenvd("TINT2_BUTTON_ALIGNED_X2", aligned_x2);
setenvd("TINT2_BUTTON_ALIGNED_Y2", aligned_y2);
setenvd("TINT2_BUTTON_PANEL_X1", panel_x1);
setenvd("TINT2_BUTTON_PANEL_Y1", panel_y1);
setenvd("TINT2_BUTTON_PANEL_X2", panel_x2);
setenvd("TINT2_BUTTON_PANEL_Y2", panel_y2);
} else {
setenv("TINT2_CONFIG", config_path, 1);
}
command = g_strdup_printf("export TINT2_CONFIG=%s;"
"%s",
config_path,
command);
if (!command)
return;
return -1;
if (!tooltip)
tooltip = command;
#if HAVE_SN && !defined TINT2CONF
#if HAVE_SN
SnLauncherContext *ctx = 0;
if (startup_notifications && time) {
if (startup_notifications && startup_notification && time) {
ctx = sn_launcher_context_new(server.sn_display, server.screen);
sn_launcher_context_set_name(ctx, tooltip);
sn_launcher_context_set_description(ctx, "Application launched from tint2");
@@ -131,11 +351,11 @@ void tint_exec(const char *command, const char *dir, const char *tooltip, Time t
pid_t pid;
pid = fork();
if (pid < 0) {
fprintf(stderr, "Could not fork\n");
fprintf(stderr, "tint2: Could not fork\n");
} else if (pid == 0) {
// Child process
#if HAVE_SN && !defined TINT2CONF
if (startup_notifications && time) {
#if HAVE_SN
if (startup_notifications && startup_notification && time) {
sn_launcher_context_setup_child_process(ctx);
}
#endif // HAVE_SN
@@ -144,36 +364,73 @@ void tint_exec(const char *command, const char *dir, const char *tooltip, Time t
// Run the command
if (dir)
chdir(dir);
execl("/bin/sh", "/bin/sh", "-c", command, NULL);
fprintf(stderr, "Failed to execlp %s\n", command);
#if HAVE_SN && !defined TINT2CONF
if (startup_notifications && time) {
close_all_fds();
reset_signals();
if (terminal) {
#if !defined(__OpenBSD__)
fprintf(stderr, "tint2: executing in x-terminal-emulator: %s\n", command);
wordexp_t words;
words.we_offs = 2;
if (wordexp(command, &words, WRDE_DOOFFS | WRDE_SHOWERR) == 0) {
words.we_wordv[0] = (char *)"x-terminal-emulator";
words.we_wordv[1] = (char *)"-e";
execvp("x-terminal-emulator", words.we_wordv);
}
#endif
fprintf(stderr,
"tint2: could not execute command in x-terminal-emulator: %s, executting in shell\n",
command);
}
execlp("sh", "sh", "-c", command, NULL);
fprintf(stderr, "tint2: Failed to execute %s\n", command);
#if HAVE_SN
if (startup_notifications && startup_notification && time) {
sn_launcher_context_unref(ctx);
}
#endif // HAVE_SN
_exit(1);
} else {
// Parent process
#if HAVE_SN && !defined TINT2CONF
if (startup_notifications && time) {
#if HAVE_SN
if (startup_notifications && startup_notification && time) {
g_tree_insert(server.pids, GINT_TO_POINTER(pid), ctx);
}
#endif // HAVE_SN
}
unsetenv("TINT2_CONFIG");
unsetenv("TINT2_BUTTON_X");
unsetenv("TINT2_BUTTON_Y");
unsetenv("TINT2_BUTTON_W");
unsetenv("TINT2_BUTTON_H");
unsetenv("TINT2_BUTTON_ALIGNED_X");
unsetenv("TINT2_BUTTON_ALIGNED_Y");
unsetenv("TINT2_BUTTON_ALIGNED_X1");
unsetenv("TINT2_BUTTON_ALIGNED_Y1");
unsetenv("TINT2_BUTTON_ALIGNED_X2");
unsetenv("TINT2_BUTTON_ALIGNED_Y2");
unsetenv("TINT2_BUTTON_PANEL_X1");
unsetenv("TINT2_BUTTON_PANEL_Y1");
unsetenv("TINT2_BUTTON_PANEL_X2");
unsetenv("TINT2_BUTTON_PANEL_Y2");
return pid;
}
void tint_exec_no_sn(const char *command)
{
tint_exec(command, NULL, NULL, 0);
tint_exec(command, NULL, NULL, 0, NULL, 0, 0, FALSE, FALSE);
}
#endif
char *expand_tilde(const char *s)
{
const gchar *home = g_get_home_dir();
if (home && (strcmp(s, "~") == 0 || strstr(s, "~/") == s)) {
char *result = calloc(strlen(home) + strlen(s), 1);
strcat(result, home);
strcat(result, s + 1);
size_t buf_size = strlen(home) + strlen(s);
char *result = calloc(buf_size, 1);
strlcat(result, home, buf_size);
strlcat(result, s + 1, buf_size);
return result;
} else {
return strdup(s);
@@ -186,14 +443,16 @@ char *contract_tilde(const char *s)
if (!home)
return strdup(s);
char *home_slash = calloc(strlen(home) + 2, 1);
strcat(home_slash, home);
strcat(home_slash, "/");
size_t buf_size = strlen(home) + 2;
char *home_slash = calloc(buf_size, 1);
strlcat(home_slash, home, buf_size);
strlcat(home_slash, "/", buf_size);
if ((strcmp(s, home) == 0 || strstr(s, home_slash) == s)) {
char *result = calloc(strlen(s) - strlen(home) + 2, 1);
strcat(result, "~");
strcat(result, s + strlen(home));
size_t buf_size2 = strlen(s) - strlen(home) + 2;
char *result = calloc(buf_size2, 1);
strlcat(result, "~", buf_size2);
strlcat(result, s + strlen(home), buf_size2);
free(home_slash);
return result;
} else {
@@ -495,15 +754,15 @@ void draw_text(PangoLayout *layout, cairo_t *c, int posx, int posy, Color *color
Imlib_Image load_image(const char *path, int cached)
{
Imlib_Image image;
static unsigned long counter = 0;
if (debug_icons)
fprintf(stderr, "tint2: loading icon %s\n", path);
#ifdef HAVE_RSVG
if (cached) {
image = imlib_load_image_immediately(path);
} else {
image = imlib_load_image_immediately_without_cache(path);
}
image = imlib_load_image(path);
if (!image && g_str_has_suffix(path, ".svg")) {
char tmp_filename[128];
sprintf(tmp_filename, "/tmp/tint2-%d.png", (int)getpid());
snprintf(tmp_filename, sizeof(tmp_filename), "/tmp/tint2-%d-%lu.png", (int)getpid(), counter);
counter++;
int fd = open(tmp_filename, O_CREAT | O_EXCL, 0600);
if (fd >= 0) {
// We fork here because librsvg allocates memory like crazy
@@ -514,30 +773,28 @@ Imlib_Image load_image(const char *path, int cached)
RsvgHandle *svg = rsvg_handle_new_from_file(path, &err);
if (err != NULL) {
fprintf(stderr, "Could not load svg image!: %s", err->message);
fprintf(stderr, "tint2: Could not load svg image!: %s", err->message);
g_error_free(err);
} else {
GdkPixbuf *pixbuf = rsvg_handle_get_pixbuf(svg);
gdk_pixbuf_save(pixbuf, tmp_filename, "png", NULL, NULL);
}
exit(0);
_exit(0);
} else {
// Parent
close(fd);
waitpid(pid, 0, 0);
image = imlib_load_image_immediately_without_cache(tmp_filename);
image = imlib_load_image_immediately(tmp_filename);
unlink(tmp_filename);
}
}
} else
#endif
{
if (cached) {
image = imlib_load_image_immediately(path);
} else {
image = imlib_load_image_immediately_without_cache(path);
}
image = imlib_load_image(path);
}
imlib_context_set_image(image);
imlib_image_set_changes_on_disk();
return image;
}
@@ -631,48 +888,104 @@ void clear_pixmap(Pixmap p, int x, int y, int w, int h)
XRenderFreePicture(server.display, pict);
}
void get_text_size2(PangoFontDescription *font,
int *height_ink,
int *height,
int *width,
int panel_height,
int panel_width,
char *text,
int len,
PangoWrapMode wrap,
PangoEllipsizeMode ellipsis,
gboolean markup)
void get_text_size(const PangoFontDescription *font,
int *height,
int *width,
int available_height,
int available_width,
const char *text,
int text_len,
PangoWrapMode wrap,
PangoEllipsizeMode ellipsis,
PangoAlignment alignment,
gboolean markup,
double scale)
{
PangoRectangle rect_ink, rect;
Pixmap pmap = XCreatePixmap(server.display, server.root_win, panel_height, panel_width, server.depth);
available_width = MAX(0, available_width);
available_height = MAX(0, available_height);
cairo_surface_t *cs = cairo_xlib_surface_create(server.display, pmap, server.visual, panel_height, panel_width);
Pixmap pmap = XCreatePixmap(server.display, server.root_win, available_height, available_width, server.depth);
cairo_surface_t *cs =
cairo_xlib_surface_create(server.display, pmap, server.visual, available_height, available_width);
cairo_t *c = cairo_create(cs);
PangoLayout *layout = pango_cairo_create_layout(c);
pango_layout_set_width(layout, panel_width * PANGO_SCALE);
pango_layout_set_height(layout, panel_height * PANGO_SCALE);
PangoContext *context = pango_cairo_create_context(c);
pango_cairo_context_set_resolution(context, 96 * scale);
PangoLayout *layout = pango_layout_new(context);
pango_layout_set_width(layout, available_width * PANGO_SCALE);
pango_layout_set_height(layout, available_height * PANGO_SCALE);
pango_layout_set_alignment(layout, alignment);
pango_layout_set_wrap(layout, wrap);
pango_layout_set_ellipsize(layout, ellipsis);
pango_layout_set_font_description(layout, font);
text_len = MAX(0, text_len);
if (!markup)
pango_layout_set_text(layout, text, len);
pango_layout_set_text(layout, text, text_len);
else
pango_layout_set_markup(layout, text, len);
pango_layout_set_markup(layout, text, text_len);
pango_layout_get_pixel_extents(layout, &rect_ink, &rect);
*height_ink = rect_ink.height;
*height = rect.height;
*width = rect.width;
// printf("dimension : %d - %d\n", rect_ink.height, rect.height);
// fprintf(stderr, "tint2: dimension : %d - %d\n", rect_ink.height, rect.height);
g_object_unref(layout);
g_object_unref(context);
cairo_destroy(c);
cairo_surface_destroy(cs);
XFreePixmap(server.display, pmap);
}
void get_text_size2(const PangoFontDescription *font,
int *height,
int *width,
int available_height,
int available_width,
const char *text,
int text_len,
PangoWrapMode wrap,
PangoEllipsizeMode ellipsis,
PangoAlignment alignment,
gboolean markup,
double scale)
{
get_text_size(font, height, width, available_height, available_width, text, text_len, wrap, ellipsis, alignment, markup, scale);
// We do multiple passes, because pango sucks
int actual_height, actual_width, overflow = 0;
while (true) {
get_text_size(font, &actual_height, &actual_width, *height, *width, text, text_len, wrap, ellipsis, alignment, markup, scale);
if (actual_height <= *height)
break;
if (*width >= available_width)
break;
overflow = 1;
fprintf(stderr, "tint2: text overflows, recomputing: available %dx%d, computed %dx%d, actual %dx%d: %s\n",
available_width,
available_height,
*width,
*height,
actual_width,
actual_height,
text);
(*width)++;
}
if (overflow) {
*height = actual_height;
fprintf(stderr, "tint2: text final size computed as: available %dx%d, computed %dx%d, actual %dx%d: %s\n",
available_width,
available_height,
*width,
*height,
actual_width,
actual_height,
text);
}
}
#if !GLIB_CHECK_VERSION(2, 34, 0)
GList *g_list_copy_deep(GList *list, GCopyFunc func, gpointer user_data)
{
@@ -740,3 +1053,142 @@ gint cmp_ptr(gconstpointer a, gconstpointer b)
else
return 1;
}
void close_all_fds()
{
long maxfd = sysconf(_SC_OPEN_MAX);
for (int fd = 3; fd < maxfd; fd++) {
close(fd);
}
}
GString *tint2_g_string_replace(GString *s, const char *from, const char *to)
{
GString *result = g_string_new("");
for (char *p = s->str; *p;) {
if (strstr(p, from) == p) {
g_string_append(result, to);
p += strlen(from);
} else {
g_string_append_c(result, *p);
p += 1;
}
}
g_string_assign(s, result->str);
g_string_free(result, TRUE);
return s;
}
void get_image_mean_color(const Imlib_Image image, Color *mean_color)
{
bzero(mean_color, sizeof(*mean_color));
if (!image)
return;
imlib_context_set_image(image);
imlib_image_set_has_alpha(1);
size_t size = (size_t)imlib_image_get_width() * (size_t)imlib_image_get_height();
DATA32 *data = imlib_image_get_data_for_reading_only();
DATA32 sum_r, sum_g, sum_b, count;
sum_r = sum_g = sum_b = count = 0;
for (size_t i = 0; i < size; i++) {
DATA32 argb, a, r, g, b;
argb = data[i];
a = (argb >> 24) & 0xff;
r = (argb >> 16) & 0xff;
g = (argb >> 8) & 0xff;
b = (argb) & 0xff;
if (a) {
sum_r += r;
sum_g += g;
sum_b += b;
count++;
}
}
if (!count)
count = 1;
mean_color->alpha = 1.0;
mean_color->rgb[0] = sum_r / 255.0 / count;
mean_color->rgb[1] = sum_g / 255.0 / count;
mean_color->rgb[2] = sum_b / 255.0 / count;
}
void adjust_color(Color *color, int alpha, int saturation, int brightness)
{
if (alpha == 100 && saturation == 0 && brightness == 0)
return;
DATA32 argb = (((DATA32)(color->alpha * 255) & 0xff) << 24) |
(((DATA32)(color->rgb[0] * 255) & 0xff) << 16) |
(((DATA32)(color->rgb[1] * 255) & 0xff) << 8) |
(((DATA32)(color->rgb[2] * 255) & 0xff) << 0);
adjust_asb(&argb, 1, 1, alpha / 100.0, saturation / 100.0, brightness / 100.0);
DATA32 a = (argb >> 24) & 0xff;
DATA32 r = (argb >> 16) & 0xff;
DATA32 g = (argb >> 8) & 0xff;
DATA32 b = (argb) & 0xff;
color->alpha = a / 255.;
color->rgb[0] = r / 255.;
color->rgb[1] = g / 255.;
color->rgb[2] = b / 255.;
}
void dump_image_data(const char *file_name, const char *name)
{
Imlib_Image image = load_image(file_name, false);
if (!image) {
fprintf(stderr, "tint2: Could not load image from file\n");
return;
}
gchar *header_name = g_strdup_printf("%s.h", name);
gchar *guard = g_strdup_printf("%s_h", name);
FILE *header = fopen(header_name, "wt");
fprintf(header,
"#ifndef %s\n"
"#define %s\n"
"\n"
"#include <Imlib2.h>\n"
"\n"
"extern int %s_width;\n"
"extern int %s_height;\n"
"extern DATA32 %s_data[];\n"
"\n"
"#endif\n",
guard,
guard,
name,
name,
name);
fclose(header);
g_free(guard);
g_free(header_name);
imlib_context_set_image(image);
gchar *source_name = g_strdup_printf("%s.c", name);
FILE *source = fopen(source_name, "wt");
fprintf(source,
"#include <%s.h>\n"
"\n"
"int %s_width = %d;\n"
"int %s_height = %d;\n"
"DATA32 %s_data[] = {",
name,
name,
imlib_image_get_width(),
name,
imlib_image_get_height(),
name);
size_t size = (size_t)imlib_image_get_width() * (size_t)imlib_image_get_height();
DATA32 *data = imlib_image_get_data_for_reading_only();
for (size_t i = 0; i < size; i++) {
fprintf(source, "%s%u", i == 0 ? "" : ", ", data[i]);
}
fprintf(source, "};\n");
fclose(source);
g_free(source_name);
imlib_free_image();
}

View File

@@ -7,17 +7,14 @@
#define COMMON_H
#define WM_CLASS_TINT "panel"
#define TINT2_PANGO_SLACK 0
#include <glib.h>
#include <Imlib2.h>
#include <pango/pangocairo.h>
#include "area.h"
#define GREEN "\033[1;32m"
#define YELLOW "\033[1;33m"
#define RED "\033[1;31m"
#define BLUE "\033[1;34m"
#define RESET "\033[0m"
#include "colors.h"
#include "strlcat.h"
#define MAX3(a, b, c) MAX(MAX(a, b), c)
#define MIN3(a, b, c) MIN(MIN(a, b), c)
@@ -41,6 +38,18 @@ typedef enum MouseAction {
#define ALL_DESKTOPS 0xFFFFFFFF
void write_string(int fd, const char *s);
void log_string(int fd, const char *s);
void dump_backtrace(int log_fd);
// sleep() returns early when signals arrive. This function does not.
void safe_sleep(int seconds);
const char *signal_name(int sig);
const char *get_home_dir();
// Copies a file to another path
void copy_file(const char *path_src, const char *path_dest);
@@ -54,8 +63,17 @@ void extract_values(const char *value, char **value1, char **value2, char **valu
void extract_values_4(const char *value, char **value1, char **value2, char **value3, char **value4);
// Executes a command in a shell.
void tint_exec(const char *command, const char *dir, const char *tooltip, Time time);
pid_t tint_exec(const char *command,
const char *dir,
const char *tooltip,
Time time,
Area *area,
int x,
int y,
gboolean terminal,
gboolean startup_notification);
void tint_exec_no_sn(const char *command);
int setenvd(const char *name, const int value);
// Returns a copy of s in which "~" is expanded to the path to the user's home directory.
// The caller takes ownership of the string.
@@ -88,23 +106,25 @@ Imlib_Image load_image(const char *path, int cached);
// * 1 = white
void adjust_asb(DATA32 *data, int w, int h, float alpha_adjust, float satur_adjust, float bright_adjust);
Imlib_Image adjust_icon(Imlib_Image original, int alpha, int saturation, int brightness);
void adjust_color(Color *color, int alpha, int saturation, int brightness);
void create_heuristic_mask(DATA32 *data, int w, int h);
// Renders the current Imlib image to a drawable. Wrapper around imlib_render_image_on_drawable.
void render_image(Drawable d, int x, int y);
void get_text_size2(PangoFontDescription *font,
int *height_ink,
void get_text_size2(const PangoFontDescription *font,
int *height,
int *width,
int panel_height,
int panel_with,
char *text,
int len,
int available_height,
int available_with,
const char *text,
int text_len,
PangoWrapMode wrap,
PangoEllipsizeMode ellipsis,
gboolean markup);
PangoAlignment alignment,
gboolean markup,
double scale);
void draw_text(PangoLayout *layout, cairo_t *c, int posx, int posy, Color *color, int font_shadow);
@@ -115,6 +135,8 @@ void draw_rect_on_sides(cairo_t *c, double x, double y, double w, double h, doub
// Clears the pixmap (with transparent color)
void clear_pixmap(Pixmap p, int x, int y, int w, int h);
void close_all_fds();
// Appends to the list locations all the directories contained in the environment variable var (split by ":").
// Optional suffixes are added to each directory. The suffix arguments MUST end with NULL.
// Returns the new value of the list.
@@ -125,6 +147,12 @@ GSList *slist_remove_duplicates(GSList *list, GCompareFunc eq, GDestroyNotify fr
// A trivial pointer comparator.
gint cmp_ptr(gconstpointer a, gconstpointer b);
GString *tint2_g_string_replace(GString *s, const char *from, const char *to);
void get_image_mean_color(const Imlib_Image image, Color *mean_color);
void dump_image_data(const char *file_name, const char *name);
#define free_and_null(p) \
{ \
free(p); \

View File

@@ -0,0 +1,97 @@
/**************************************************************************
*
* Copyright (C) 2017 tint2 authors
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License version 2
* as published by the Free Software Foundation.
*
* This program 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 General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
**************************************************************************/
#include <stdlib.h>
#include "fps_distribution.h"
static float *fps_distribution = NULL;
void init_fps_distribution()
{
// measure FPS with resolution:
// 0-59: 1 (60 samples)
// 60-199: 10 (14)
// 200-1,999: 25 (72)
// 1k-19,999: 1000 (19)
// 20x+: inf (1)
// => 166 samples
if (fps_distribution)
return;
fps_distribution = calloc(170, sizeof(float));
}
void cleanup_fps_distribution()
{
free(fps_distribution);
fps_distribution = NULL;
}
void sample_fps(double fps)
{
int fps_rounded = (int)(fps + 0.5);
int i = 1;
if (fps_rounded < 60) {
i += fps_rounded;
} else {
i += 60;
if (fps_rounded < 200) {
i += (fps_rounded - 60) / 10;
} else {
i += 14;
if (fps_rounded < 2000) {
i += (fps_rounded - 200) / 25;
} else {
i += 72;
if (fps_rounded < 20000) {
i += (fps_rounded - 2000) / 1000;
} else {
i += 20;
}
}
}
}
// fprintf(stderr, "tint2: fps = %.0f => i = %d\n", fps, i);
fps_distribution[i] += 1.;
fps_distribution[0] += 1.;
}
void fps_compute_stats(double *low, double *median, double *high, double *samples)
{
*median = *low = *high = *samples = -1;
if (!fps_distribution || fps_distribution[0] < 1)
return;
float total = fps_distribution[0];
*samples = (double)fps_distribution[0];
float cum_low = 0.05f * total;
float cum_median = 0.5f * total;
float cum_high = 0.95f * total;
float cum = 0;
for (int i = 1; i <= 166; i++) {
double value =
(i < 60) ? i : (i < 74) ? (60 + (i - 60) * 10) : (i < 146) ? (200 + (i - 74) * 25)
: (i < 165) ? (2000 + (i - 146) * 1000) : 20000;
// fprintf(stderr, "tint2: %6.0f (i = %3d) : %.0f | ", value, i, (double)fps_distribution[i]);
cum += fps_distribution[i];
if (*low < 0 && cum >= cum_low)
*low = value;
if (*median < 0 && cum >= cum_median)
*median = value;
if (*high < 0 && cum >= cum_high)
*high = value;
}
}

View File

@@ -0,0 +1,9 @@
#ifndef FPS_DISTRIBUTION_H
#define FPS_DISTRIBUTION_H
void init_fps_distribution();
void cleanup_fps_distribution();
void sample_fps(double fps);
void fps_compute_stats(double *low, double *median, double *high, double *samples);
#endif

View File

@@ -40,7 +40,7 @@ GradientType gradient_type_from_string(const char *str)
return GRADIENT_VERTICAL;
if (g_str_equal(str, "radial"))
return GRADIENT_CENTERED;
fprintf(stderr, RED "Invalid gradient type: %s" RESET "\n", str);
fprintf(stderr, RED "tint2: Invalid gradient type: %s" RESET "\n", str);
return GRADIENT_VERTICAL;
}

View File

@@ -67,11 +67,10 @@ void cleanup_gradient(GradientClass *g);
// Gradient instances associated to Areas
struct Area;
typedef struct Area Area;
typedef struct GradientInstance {
GradientClass *gradient_class;
Area *area;
struct Area *area;
cairo_pattern_t *pattern;
} GradientInstance;

331
src/util/mem.c Normal file
View File

@@ -0,0 +1,331 @@
#define _GNU_SOURCE
#include <dlfcn.h>
#include <endian.h>
#include <fcntl.h>
#include <pthread.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/time.h>
#include <sys/types.h>
#include <time.h>
#include <unistd.h>
#include <sys/mman.h>
#include <limits.h>
#include <signal.h>
#include "bt.h"
#include "bool.h"
#define UNUSED(x) ((void)x)
void ERR(const char *s)
{
if (!s) {
ERR("(null)");
return;
}
ssize_t ret = write(STDERR_FILENO, s, strlen(s));
UNUSED(ret);
ret = fsync(STDERR_FILENO);
UNUSED(ret);
}
void utoa(unsigned long n, char *s)
{
if (n == 0) {
*s++ = '0';
*s = 0;
return;
}
char buffer[128] = {0};
char *digit;
for (digit = buffer; n; digit++, n/=10) {
*digit = '0' + (n % 10);
}
digit--;
while (digit >= buffer) {
*s++ = *digit--;
}
*s = 0;
}
unsigned long uabs(long n)
{
if (n == LONG_MIN)
return ((unsigned long)LONG_MAX) + 1;
return (unsigned long)labs(n);
}
void itoa(long n, char *s)
{
if (n < 0) {
*s++ = '-';
}
utoa(uabs(n), s);
}
__attribute__((noreturn))
static void crash(char *file, int line, char *msg)
{
ERR(file);
ERR(":");
char buf[256];
itoa(line, buf);
ERR(buf);
ERR(" ");
ERR(msg);
_exit(1);
}
#define ASSERT_OK(call) if (0 != (call)) crash(__FILE__, __LINE__, #call)
#define ASSERT_FD(fd) if ((fd) == -1) crash(__FILE__, __LINE__, "bad file descriptor")
#define ASSERT_PID(pid) if ((pid) == -1) crash(__FILE__, __LINE__, "bad PID")
#define ASSERT(b) if (!(b)) crash(__FILE__, __LINE__, "assert failed")
void *return_null(void *x, void *y)
{
return 0;
}
void load_func_or_crash(void **result, const char *name)
{
static int inside_loader = 0;
if (inside_loader) {
*result = (void*)return_null;
return;
} else if (*result == (void*)return_null) {
*result = 0;
}
if (*result)
return;
dlerror();
inside_loader++;
*result = dlsym(RTLD_NEXT, name);
inside_loader--;
char *err = dlerror();
if (err) {
ERR("Failed to load ");
ERR(name);
ERR(" error: ");
ERR(err);
ERR("\n");
exit(1);
}
}
static int fd = -1;
static u_int64_t tstart = 0;
static bool stop_alloc_log = false;
static void write_char(char c)
{
static char buffer[4096] = {0};
static size_t count = 0;
if (c) {
buffer[count++] = c;
}
if (!count)
return;
if (c == '\n' || c == 0 || count >= sizeof(buffer)) {
ssize_t ret = write(fd, buffer, count);
ASSERT(ret > 0);
count = 0;
}
}
static void write_string(const char *s)
{
for (; *s; s++)
write_char(*s);
}
static void write_word(const char *s)
{
write_string(s);
write_string(" ");
}
static char hex(u_int8_t value)
{
if (value < 10)
return '0' + (char)value;
return 'a' + (char)(value - 10);
}
static void write_hex(u_int64_t v)
{
write_string("0x");
if (!v) {
write_string("0 ");
return;
}
v = htobe64(v);
int leading_zero = 1;
while (v) {
u_int8_t byte = v & 0xff;
if (byte)
leading_zero = 0;
if (!leading_zero) {
write_char(hex(byte >> 4));
write_char(hex(byte & 0xf));
}
v = v >> 8;
}
write_string(" ");
}
static void write_backtrace(int skip)
{
struct backtrace bt;
get_backtrace(&bt, skip + 1);
for (size_t i = 0; i < bt.frame_count; i++) {
write_word(bt.frames[i].name);
}
}
static void log_alloc_finish(void);
static u_int64_t current_time_ms()
{
struct timespec t = {0, 0};
clock_gettime(CLOCK_MONOTONIC, &t);
u_int64_t result = t.tv_sec * 1000 + t.tv_nsec / 1000 / 1000;
return result;
}
static void log_alloc_init(u_int64_t t)
{
if (stop_alloc_log)
return;
if (fd == -1) {
int pfd[2] = {-1, -1};
ASSERT_OK(pipe(pfd));
pid_t child = fork();
ASSERT_PID(child);
if (child == 0) {
// child
close(pfd[1]);
ASSERT_FD(dup2(pfd[0], STDIN_FILENO));
ASSERT_OK(close(pfd[0]));
int out = open("mem.log.gz", O_APPEND | O_CLOEXEC | O_CREAT | O_WRONLY | O_TRUNC, 0600);
ASSERT_FD(out);
ASSERT_FD(dup2(out, STDOUT_FILENO));
sigset_t mask;
sigfillset(&mask);
sigprocmask(SIG_SETMASK, &mask, NULL);
ASSERT_OK(execlp("gzip", "gzip", "-c", NULL));
_exit(1);
} else {
// parent
close(pfd[0]);
fd = pfd[1];
}
atexit(log_alloc_finish);
tstart = t;
write_string("# function time_ms result ptr size count backtrace\n");
}
}
static void log_alloc_locked(const char *func_name, void *result, void *ptr, size_t size, size_t count)
{
if (stop_alloc_log)
return;
u_int64_t t = current_time_ms();
if (fd == -1)
log_alloc_init(t);
if (fd == -1)
return;
if (func_name) {
write_word(func_name);
write_hex((u_int64_t)(t - tstart));
write_hex((u_int64_t)result);
write_hex((u_int64_t)ptr);
write_hex((u_int64_t)size);
write_hex((u_int64_t)count);
write_backtrace(2);
write_string("\n");
} else {
write_string("# done\n");
close(fd);
fd = -1;
stop_alloc_log = true;
}
}
static int pid = -1;
static void log_alloc(const char *func_name, void *result, void *ptr, size_t size, size_t count)
{
static pthread_mutex_t mutex_global = PTHREAD_MUTEX_INITIALIZER;
static pthread_mutex_t mutex_recursive;
static pthread_mutex_t mutex_nonrecursive = PTHREAD_MUTEX_INITIALIZER;
static bool mutexes_initialized = false;
pthread_mutex_lock(&mutex_global);
{
if (!mutexes_initialized) {
pthread_mutexattr_t attr;
pthread_mutexattr_init(&attr);
pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE);
pthread_mutex_init(&mutex_recursive, &attr);
mutexes_initialized = true;
pid = getpid();
}
}
pthread_mutex_unlock(&mutex_global);
// Do not log from forked processes.
if (pid != getpid())
return;
pthread_mutex_lock(&mutex_recursive);
int ret = pthread_mutex_trylock(&mutex_nonrecursive);
if (ret == 0) {
log_alloc_locked(func_name, result, ptr, size, count);
pthread_mutex_unlock(&mutex_nonrecursive);
}
pthread_mutex_unlock(&mutex_recursive);
}
static void log_alloc_finish()
{
log_alloc(0, 0, 0, 0, 0);
}
void *malloc(size_t size)
{
static void *(*original)(size_t size) = 0;
load_func_or_crash((void *)&original, __FUNCTION__);
void *result = original(size);
log_alloc(__FUNCTION__, result, 0, size, 0);
return result;
}
void *realloc(void *ptr, size_t size)
{
static void *(*original)(void *p, size_t size) = 0;
load_func_or_crash((void *)&original, __FUNCTION__);
void *result = original(ptr, size);
log_alloc(__FUNCTION__, result, ptr, size, 0);
return result;
}
void *calloc(size_t nmemb, size_t size)
{
static void *(*original)(size_t nmemb, size_t size) = 0;
load_func_or_crash((void *)&original, __FUNCTION__);
void *result = original(nmemb, size);
log_alloc(__FUNCTION__, result, 0, size, nmemb);
return result;
}
void free(void *ptr)
{
static void *(*original)(void *p) = 0;
load_func_or_crash((void *)&original, __FUNCTION__);
if (!original) {
return;
}
original(ptr);
log_alloc(__FUNCTION__, 0, ptr, 0, 0);
}

83
src/util/print.c Normal file
View File

@@ -0,0 +1,83 @@
#include <stdio.h>
#include "print.h"
int print_uchar(unsigned char v)
{
return printf("%u", v);
}
int print_char(char v)
{
return printf("%c", v);
}
int print_short(short v)
{
return printf("%d", v);
}
int print_ushort(unsigned short v)
{
return printf("%u", v);
}
int print_int(int v)
{
return printf("%d", v);
}
int print_uint(unsigned v)
{
return printf("%u", v);
}
int print_long(long v)
{
return printf("%ld", v);
}
int print_ulong(unsigned long v)
{
return printf("%lu", v);
}
int print_long_long(long long v)
{
return printf("%lld", v);
}
int print_ulong_long(unsigned long long v)
{
return printf("%llu", v);
}
int print_float(float v)
{
return printf("%f", (double)v);
}
int print_double(double v)
{
return printf("%f", v);
}
int print_long_double(long double v)
{
return printf("%Lf", v);
}
int print_string(char *s)
{
return printf("%s", s);
}
int print_pointer(void *v)
{
return printf("%p", v);
}
int print_unknown()
{
return printf("(variable of unknown type)");
}

61
src/util/print.h Normal file
View File

@@ -0,0 +1,61 @@
#ifndef PRINT_H
#define PRINT_H
#ifdef HAS_GENERIC
int print_uchar(unsigned char v);
int print_char(char v);
int print_short(short v);
int print_ushort(unsigned short v);
int print_int(int v);
int print_uint(unsigned v);
int print_long(long v);
int print_ulong(unsigned long v);
int print_long_long(long long v);
int print_ulong_long(unsigned long long v);
int print_float(float v);
int print_double(double v);
int print_long_double(long double v);
int print_string(char *s);
int print_pointer(void *v);
int print_unknown();
#define print(x) \
_Generic((x), \
unsigned char: print_uchar, \
char: print_char, \
short int: print_short, \
unsigned short int: print_ushort, \
int: print_int, \
unsigned int: print_uint, \
long int: print_long, \
unsigned long int: print_ulong, \
long long int: print_long_long, \
unsigned long long int: print_ulong_long, \
float: print_float, \
double: print_double, \
long double: print_long_double, \
char *: print_string, \
void *: print_pointer, \
default : print_unknown)(x)
#else
#define print(...) printf("Omitted, the compiler does not support C11 generics.\n")
#endif
#endif

View File

@@ -18,21 +18,24 @@
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
**************************************************************************/
#include <X11/extensions/Xrender.h>
#include <X11/extensions/Xdamage.h>
#include <X11/extensions/Xrandr.h>
#include <X11/extensions/Xrender.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include "server.h"
#include "common.h"
#include "config.h"
#include "server.h"
#include "signals.h"
#include "window.h"
Server server;
gboolean primary_monitor_first = FALSE;
void server_catch_error(Display *d, XErrorEvent *ev)
{
}
@@ -120,6 +123,14 @@ void server_init_atoms()
server.atom.TARGETS = XInternAtom(server.display, "TARGETS", False);
}
const char *GetAtomName(Display *disp, Atom a)
{
if (a == None)
return "None";
else
return XGetAtomName(disp, a);
}
void cleanup_server()
{
if (server.colormap)
@@ -250,7 +261,7 @@ void get_root_pixmap()
server.root_pmap = ret;
if (server.root_pmap == None) {
fprintf(stderr, "tint2 : pixmap background detection failed\n");
fprintf(stderr, "tint2: pixmap background detection failed\n");
} else {
XGCValues gcv;
gcv.ts_x_origin = 0;
@@ -268,13 +279,6 @@ int compare_monitor_pos(const void *monitor1, const void *monitor2)
const Monitor *m1 = (const Monitor *)monitor1;
const Monitor *m2 = (const Monitor *)monitor2;
if (primary_monitor_first) {
if (m1->primary && !m2->primary)
return -1;
if (!m1->primary && m2->primary)
return 1;
}
if (m1->x < m2->x) {
return -1;
} else if (m1->x > m2->x) {
@@ -307,6 +311,21 @@ void sort_monitors()
qsort(server.monitors, server.num_monitors, sizeof(Monitor), compare_monitor_pos);
}
int compute_dpi(XRRCrtcInfo *crtc, XRROutputInfo *output)
{
double width = output->mm_width;
double height = output->mm_height;
double x_res = crtc->width;
double y_res = crtc->height;
if (width > 0 && height > 0) {
int dpi_x = x_res / width * 25.4;
int dpi_y = y_res / height * 25.4;
return MAX(dpi_x, dpi_y);
}
return 0;
}
void get_monitors()
{
if (XineramaIsActive(server.display)) {
@@ -317,14 +336,14 @@ void get_monitors()
if (res && res->ncrtc >= num_monitors) {
// use xrandr to identify monitors (does not work with proprietery nvidia drivers)
printf("xRandr: Found crtc's: %d\n", res->ncrtc);
fprintf(stderr, "tint2: xRandr: Found crtc's: %d\n", res->ncrtc);
server.monitors = calloc(res->ncrtc, sizeof(Monitor));
num_monitors = 0;
for (int i = 0; i < res->ncrtc; ++i) {
XRRCrtcInfo *crtc_info = XRRGetCrtcInfo(server.display, res, res->crtcs[i]);
// Ignore empty crtc
if (!crtc_info->width || !crtc_info->height) {
printf("xRandr: crtc %d seems disabled\n", i);
fprintf(stderr, "tint2: xRandr: crtc %d seems disabled\n", i);
XRRFreeCrtcInfo(crtc_info);
continue;
}
@@ -335,12 +354,22 @@ void get_monitors()
server.monitors[i_monitor].width = crtc_info->width;
server.monitors[i_monitor].height = crtc_info->height;
server.monitors[i_monitor].names = calloc((crtc_info->noutput + 1), sizeof(gchar *));
server.monitors[i_monitor].dpi = 96;
for (int j = 0; j < crtc_info->noutput; ++j) {
XRROutputInfo *output_info = XRRGetOutputInfo(server.display, res, crtc_info->outputs[j]);
printf("xRandr: Linking output %s with crtc %d\n", output_info->name, i);
server.monitors[i_monitor].names[j] = g_strdup(output_info->name);
XRRFreeOutputInfo(output_info);
server.monitors[i_monitor].primary = crtc_info->outputs[j] == primary_output;
int dpi = compute_dpi(crtc_info, output_info);
if (dpi)
server.monitors[i_monitor].dpi = dpi;
fprintf(stderr,
BLUE "tint2: xRandr: Linking output %s with crtc %d, resolution %dx%d, DPI %d" RESET "\n",
output_info->name,
i,
server.monitors[i_monitor].width,
server.monitors[i_monitor].height,
server.monitors[i_monitor].dpi);
XRRFreeOutputInfo(output_info);
}
server.monitors[i_monitor].names[crtc_info->noutput] = NULL;
XRRFreeCrtcInfo(crtc_info);
@@ -353,6 +382,7 @@ void get_monitors()
server.monitors[i].width = info[i].width;
server.monitors[i].height = info[i].height;
server.monitors[i].names = NULL;
server.monitors[i].dpi = 96;
}
}
@@ -389,12 +419,13 @@ void get_monitors()
server.monitors[0].width = DisplayWidth(server.display, server.screen);
server.monitors[0].height = DisplayHeight(server.display, server.screen);
server.monitors[0].names = 0;
server.monitors[0].dpi = 96;
}
}
void print_monitors()
{
fprintf(stderr, "Number of monitors: %d\n", server.num_monitors);
fprintf(stderr, "tint2: Number of monitors: %d\n", server.num_monitors);
for (int i = 0; i < server.num_monitors; i++) {
fprintf(stderr,
"Monitor %d: x = %d, y = %d, w = %d, h = %d\n",
@@ -516,10 +547,11 @@ int get_current_desktop()
int ncols = x_screen_width / work_area_width;
// fprintf(stderr, "\n");
// fprintf(stderr, "Work area size: %d x %d\n", work_area_width, work_area_height);
// fprintf(stderr, "Viewport pos: %d x %d\n", viewport_x, viewport_y);
// fprintf(stderr, "Viewport i: %d\n", (viewport_y / work_area_height) * ncols + viewport_x / work_area_width);
// fprintf(stderr, "tint2: \n");
// fprintf(stderr, "tint2: Work area size: %d x %d\n", work_area_width, work_area_height);
// fprintf(stderr, "tint2: Viewport pos: %d x %d\n", viewport_x, viewport_y);
// fprintf(stderr, "tint2: Viewport i: %d\n", (viewport_y / work_area_height) * ncols + viewport_x /
//work_area_width);
int result = (viewport_y / work_area_height) * ncols + viewport_x / work_area_width;
return MAX(0, MIN(server.num_desktops - 1, result));
@@ -550,7 +582,7 @@ void get_desktops()
}
if (server.num_desktops == 0) {
server.num_desktops = 1;
fprintf(stderr, "warning : WM doesn't respect NETWM specs. tint2 default to 1 desktop.\n");
fprintf(stderr, "tint2: warning : WM doesn't respect NETWM specs. tint2 default to 1 desktop.\n");
}
}
@@ -594,15 +626,77 @@ void server_init_visual()
server.real_transparency = TRUE;
server.depth = 32;
printf("real transparency on... depth: %d\n", server.depth);
fprintf(stderr, "tint2: real transparency on... depth: %d\n", server.depth);
server.colormap = XCreateColormap(server.display, server.root_win, visual, AllocNone);
server.visual = visual;
} else {
// no composite manager or snapshot mode => fake transparency
server.real_transparency = FALSE;
server.depth = DefaultDepth(server.display, server.screen);
printf("real transparency off.... depth: %d\n", server.depth);
fprintf(stderr, "tint2: real transparency off.... depth: %d\n", server.depth);
server.colormap = DefaultColormap(server.display, server.screen);
server.visual = DefaultVisual(server.display, server.screen);
}
}
void server_init_xdamage()
{
XDamageQueryExtension(server.display, &server.xdamage_event_type, &server.xdamage_event_error_type);
server.xdamage_event_type += XDamageNotify;
server.xdamage_event_error_type += XDamageNotify;
}
// Forward mouse click to the desktop window
void forward_click(XEvent *e)
{
// forward the click to the desktop window (thanks conky)
XUngrabPointer(server.display, e->xbutton.time);
e->xbutton.window = server.root_win;
// icewm doesn't open under the mouse.
// and xfce doesn't open at all.
e->xbutton.x = e->xbutton.x_root;
e->xbutton.y = e->xbutton.y_root;
// fprintf(stderr, "tint2: **** %d, %d\n", e->xbutton.x, e->xbutton.y);
// XSetInputFocus(server.display, e->xbutton.window, RevertToParent, e->xbutton.time);
XSendEvent(server.display, e->xbutton.window, False, ButtonPressMask, e);
}
void handle_crash(const char *reason)
{
#ifndef DISABLE_BACKTRACE
char path[4096];
sprintf(path, "%s/.tint2-crash.log", get_home_dir());
int log_fd = open(path, O_WRONLY | O_CREAT | O_TRUNC, 0600);
log_string(log_fd, RED "tint2: crashed, reason: ");
log_string(log_fd, reason);
log_string(log_fd, RESET "\n");
dump_backtrace(log_fd);
log_string(log_fd, RED "Please create a bug report with this log output." RESET "\n");
close(log_fd);
#endif
}
void x11_io_error(Display *display)
{
handle_crash("X11 I/O error");
}
#ifdef HAVE_SN
static int error_trap_depth = 0;
void error_trap_push(SnDisplay *display, Display *xdisplay)
{
++error_trap_depth;
}
void error_trap_pop(SnDisplay *display, Display *xdisplay)
{
if (error_trap_depth == 0) {
fprintf(stderr, "tint2: Error trap underflow!\n");
return;
}
XSync(xdisplay, False); /* get all errors out of the queue */
--error_trap_depth;
}
#endif // HAVE_SN

View File

@@ -93,11 +93,21 @@ typedef struct Global_atom {
Atom TARGETS;
} Global_atom;
typedef struct Property {
unsigned char *data;
int format, nitems;
Atom type;
} Property;
// Returns the name of an Atom as string. Do not free the string.
const char *GetAtomName(Display *disp, Atom a);
typedef struct Monitor {
int x;
int y;
int width;
int height;
int dpi;
gboolean primary;
gchar **names;
} Monitor;
@@ -111,6 +121,7 @@ typedef struct Viewport {
typedef struct Server {
Display *display;
int x11_fd;
Window root_win;
Window composite_manager;
gboolean real_transparency;
@@ -135,6 +146,9 @@ typedef struct Server {
Colormap colormap;
Colormap colormap32;
Global_atom atom;
int xdamage_event_type;
int xdamage_event_error_type;
gboolean has_shm;
#ifdef HAVE_SN
SnDisplay *sn_display;
GTree *pids;
@@ -153,6 +167,10 @@ Atom server_get_atom(char *atom_name);
void server_catch_error(Display *d, XErrorEvent *ev);
void server_init_atoms();
void server_init_visual();
void server_init_xdamage();
void x11_io_error(Display *display);
void handle_crash(const char *reason);
// detect root background
void get_root_pixmap();
@@ -167,4 +185,12 @@ GSList *get_desktop_names();
int get_current_desktop();
void change_desktop(int desktop);
// Forward mouse click to the desktop window
void forward_click(XEvent *e);
#ifdef HAVE_SN
void error_trap_push(SnDisplay *display, Display *xdisplay);
void error_trap_pop(SnDisplay *display, Display *xdisplay);
#endif
#endif

170
src/util/signals.c Normal file
View File

@@ -0,0 +1,170 @@
#include <errno.h>
#include <fcntl.h>
#include <glib.h>
#ifndef TINT2CONF
#ifdef HAVE_SN
#include <libsn/sn.h>
#endif
#endif
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/wait.h>
#include <unistd.h>
#include "common.h"
#include "panel.h"
#include "launcher.h"
#include "server.h"
#include "signals.h"
static sig_atomic_t signal_pending;
void signal_handler(int sig)
{
// signal handler is light as it should be
signal_pending = sig;
}
void reset_signals()
{
for (int sig = 1; sig < 32; sig++) {
signal(sig, SIG_DFL);
}
sigset_t signal_set;
sigemptyset(&signal_set);
sigprocmask(SIG_SETMASK, &signal_set, NULL);
}
#ifndef TINT2CONF
void init_signals()
{
// Set signal handlers
signal_pending = 0;
reset_signals();
struct sigaction sa_chld = {.sa_handler = SIG_IGN};
sigaction(SIGCHLD, &sa_chld, 0);
struct sigaction sa = {.sa_handler = signal_handler, .sa_flags = SA_RESTART};
sigaction(SIGUSR1, &sa, 0);
sigaction(SIGUSR2, &sa, 0);
sigaction(SIGINT, &sa, 0);
sigaction(SIGTERM, &sa, 0);
sigaction(SIGHUP, &sa, 0);
#ifdef BACKTRACE_ON_SIGNAL
struct sigaction sa_crash = {.sa_handler = crash_handler};
sigaction(SIGSEGV, &sa_crash, 0);
sigaction(SIGFPE, &sa_crash, 0);
sigaction(SIGPIPE, &sa_crash, 0);
sigaction(SIGBUS, &sa_crash, 0);
sigaction(SIGABRT, &sa_crash, 0);
sigaction(SIGSYS, &sa_crash, 0);
#endif
}
#ifdef BACKTRACE_ON_SIGNAL
void crash_handler(int sig)
{
handle_crash(signal_name(sig));
struct sigaction sa = {.sa_handler = SIG_DFL};
sigaction(sig, &sa, 0);
raise(sig);
}
#endif
int sigchild_pipe_valid = FALSE;
int sigchild_pipe[2];
static void sigchld_handler(int sig)
{
if (!sigchild_pipe_valid)
return;
int savedErrno = errno;
ssize_t unused = write(sigchild_pipe[1], "x", 1);
(void)unused;
fsync(sigchild_pipe[1]);
errno = savedErrno;
}
void sigchld_handler_async()
{
// Wait for all dead processes
pid_t pid;
int status;
while ((pid = waitpid(-1, &status, WNOHANG)) != -1 && pid != 0) {
#ifdef HAVE_SN
if (startup_notifications) {
SnLauncherContext *ctx = (SnLauncherContext *)g_tree_lookup(server.pids, GINT_TO_POINTER(pid));
if (ctx) {
g_tree_remove(server.pids, GINT_TO_POINTER(pid));
sn_launcher_context_complete(ctx);
sn_launcher_context_unref(ctx);
}
}
#endif
for (GList *l = panel_config.execp_list; l; l = l->next) {
Execp *execp = (Execp *)l->data;
if (g_tree_lookup(execp->backend->cmd_pids, GINT_TO_POINTER(pid)))
execp_cmd_completed(execp, pid);
}
}
}
void handle_sigchld_events()
{
if (sigchild_pipe_valid) {
char buffer[1];
while (read(sigchild_pipe[0], buffer, sizeof(buffer)) > 0) {
sigchld_handler_async();
}
}
}
void init_signals_postconfig()
{
gboolean need_sigchld = FALSE;
#ifdef HAVE_SN
// Initialize startup-notification
if (startup_notifications) {
server.sn_display = sn_display_new(server.display, error_trap_push, error_trap_pop);
server.pids = g_tree_new(cmp_ptr);
need_sigchld = TRUE;
}
#endif // HAVE_SN
if (panel_config.execp_list)
need_sigchld = TRUE;
if (need_sigchld) {
// Setup a handler for child termination
if (pipe(sigchild_pipe) != 0) {
fprintf(stderr, "tint2: Creating pipe failed.\n");
} else {
fcntl(sigchild_pipe[0], F_SETFL, O_NONBLOCK | fcntl(sigchild_pipe[0], F_GETFL));
fcntl(sigchild_pipe[1], F_SETFL, O_NONBLOCK | fcntl(sigchild_pipe[1], F_GETFL));
sigchild_pipe_valid = 1;
struct sigaction act = {.sa_handler = sigchld_handler, .sa_flags = SA_RESTART};
if (sigaction(SIGCHLD, &act, 0)) {
perror("sigaction");
}
}
}
}
void emit_self_restart(const char *reason)
{
fprintf(stderr,
YELLOW "%s %d: triggering tint2 restart, reason: %s" RESET "\n",
__FILE__,
__LINE__,
reason);
signal_pending = SIGUSR1;
}
int get_signal_pending()
{
return signal_pending;
}
#endif

15
src/util/signals.h Normal file
View File

@@ -0,0 +1,15 @@
#ifndef SIGNALS_H
#define SIGNALS_H
void init_signals();
void init_signals_postconfig();
void emit_self_restart(const char *reason);
int get_signal_pending();
void reset_signals();
void handle_sigchld_events();
extern int sigchild_pipe_valid;
extern int sigchild_pipe[2];
#endif

60
src/util/strlcat.c Normal file
View File

@@ -0,0 +1,60 @@
/* $NetBSD: strlcat.c,v 1.4 2005/05/16 06:55:48 lukem Exp $ */
/* from NetBSD: strlcat.c,v 1.16 2003/10/27 00:12:42 lukem Exp */
/* from OpenBSD: strlcat.c,v 1.10 2003/04/12 21:56:39 millert Exp */
/*
* Copyright (c) 1998 Todd C. Miller <Todd.Miller@courtesan.com>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND TODD C. MILLER DISCLAIMS ALL
* WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL TODD C. MILLER BE LIABLE
* FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
* OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
* CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include <stddef.h>
#include <stdint.h>
#include <string.h>
#include "strlcat.h"
/*
* Appends src to string dst of size siz (unlike strncat, siz is the
* full size of dst, not space left). At most siz-1 characters
* will be copied. Always NUL terminates (unless siz <= strlen(dst)).
* Returns strlen(src) + MIN(siz, strlen(initial dst)).
* If retval >= siz, truncation occurred.
*/
size_t
strlcat(char *dst, const char *src, size_t siz)
{
char *d = dst;
const char *s = src;
size_t n = siz;
size_t dlen;
/* Find the end of dst and adjust bytes left but don't go past end */
while (n-- != 0 && *d != '\0')
d++;
dlen = d - dst;
n = siz - dlen;
if (n == 0)
return(dlen + strlen(s));
while (*s != '\0') {
if (n != 1) {
*d++ = *s;
n--;
}
s++;
}
*d = '\0';
return(dlen + (s - src)); /* count does not include NUL */
}

16
src/util/strlcat.h Normal file
View File

@@ -0,0 +1,16 @@
#ifndef STRLCAT_H
#define STRLCAT_H
#include <stddef.h>
#include <stdint.h>
/*
* Appends src to string dst of size siz (unlike strncat, siz is the
* full size of dst, not space left). At most siz-1 characters
* will be copied. Always NUL terminates (unless siz <= strlen(dst)).
* Returns strlen(src) + MIN(siz, strlen(initial dst)).
* If retval >= siz, truncation occurred.
*/
size_t strlcat(char *dst, const char *src, size_t siz);
#endif

183
src/util/test.c Normal file
View File

@@ -0,0 +1,183 @@
#include <fcntl.h>
#include <stddef.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <glib.h>
#include "colors.h"
#include "signals.h"
#include "test.h"
typedef struct TestListItem {
Test *test;
const char *name;
} TestListItem;
static GList *all_tests = NULL;
void register_test_(Test *test, const char *name)
{
TestListItem *item = (TestListItem *)calloc(sizeof(TestListItem), 1);
item->test = test;
item->name = name;
all_tests = g_list_append(all_tests, item);
}
static char *test_log_name_from_test_name(const char *test_name)
{
char *output_name = g_strdup_printf("test_%s.log", test_name);
char *result = strdup(output_name);
g_free(output_name);
return result;
}
static void redirect_test_output(const char *test_name)
{
char *output_name = test_log_name_from_test_name(test_name);
int fd = open(output_name, O_WRONLY | O_CREAT | O_TRUNC, 0600);
if (fd == -1)
goto err;
if (dup2(fd, STDOUT_FILENO) == -1)
goto err;
if (dup2(fd, STDERR_FILENO) == -1)
goto err;
close(fd);
free(output_name);
return;
err:
fprintf(stderr, "tint2: Could not redirect test output to file name: %s\n", output_name);
if (fd != -1)
close(fd);
free(output_name);
}
static void crash(int sig)
{
kill(getpid(), SIGSEGV);
}
__attribute__((noreturn))
static void run_test_child(TestListItem *item)
{
reset_signals();
struct sigaction sa = {.sa_handler = crash};
sigaction(SIGINT, &sa, 0);
redirect_test_output(item->name);
bool result = true;
item->test(&result);
exit(result ? EXIT_SUCCESS : EXIT_FAILURE);
}
static FILE *open_test_log(const char *test_name)
{
char *output_name = test_log_name_from_test_name(test_name);
FILE *log = fopen(output_name, "a");
free(output_name);
return log;
}
static Status run_test_parent(TestListItem *item, pid_t child)
{
FILE *log = open_test_log(item->name);
if (child == -1) {
fprintf(log, "\n" "Test failed, fork failed\n");
fclose(log);
return FAILURE;
}
int child_status;
pid_t ret_pid = waitpid(child, &child_status, 0);
if (ret_pid != child) {
fprintf(log, "\n" "Test failed, waitpid failed\n");
fclose(log);
return FAILURE;
}
if (WIFEXITED(child_status)) {
int exit_status = WEXITSTATUS(child_status);
if (exit_status == EXIT_SUCCESS) {
fprintf(log, "\n" "Test succeeded.\n");
fclose(log);
return SUCCESS;
} else {
fprintf(log, "\n" "Test failed, exit status: %d.\n", exit_status);
fclose(log);
return FAILURE;
}
} else if (WIFSIGNALED(child_status)) {
int signal = WTERMSIG(child_status);
fprintf(log, "\n" "Test failed, child killed by signal: %d.\n", signal);
fclose(log);
return FAILURE;
} else {
fprintf(log, "\n" "Test failed, waitpid failed.\n");
fclose(log);
return FAILURE;
}
}
static Status run_test(TestListItem *item)
{
pid_t pid = fork();
if (pid == 0)
run_test_child(item);
struct sigaction sa = {.sa_handler = SIG_IGN};
sigaction(SIGINT, &sa, 0);
return run_test_parent(item, pid);
}
void run_all_tests(bool verbose)
{
fprintf(stdout, BLUE "tint2: Running %d tests..." RESET "\n", g_list_length(all_tests));
size_t count = 0, succeeded = 0, failed = 0;
for (GList *l = all_tests; l; l = l->next) {
TestListItem *item = (TestListItem *)l->data;
Status status = run_test(item);
count++;
fprintf(stdout, BLUE "tint2: Test " YELLOW "%s" BLUE ": ", item->name);
if (status == SUCCESS) {
fprintf(stdout, GREEN "succeeded" RESET "\n");
succeeded++;
} else {
fprintf(stdout, RED "failed" RESET "\n");
failed++;
if (verbose) {
char *log_name = test_log_name_from_test_name(item->name);
FILE *log = fopen(log_name, "rt");
if (log) {
char buffer[4096];
size_t num_read;
while ((num_read = fread(buffer, 1, sizeof(buffer), log)) > 0) {
fwrite(buffer, 1, num_read, stdout);
}
fclose(log);
}
free(log_name);
}
}
}
if (failed == 0)
fprintf(stdout, BLUE "tint2: " GREEN "all %lu tests succeeded." RESET "\n", count);
else
fprintf(stdout, BLUE "tint2: " RED "%lu" BLUE " out of %lu tests " RED "failed." RESET "\n", failed, count);
}
#if 0
TEST(dummy) {
int x = 2;
int y = 2;
ASSERT_EQUAL(x, y);
}
TEST(dummy_bad) {
int x = 2;
int y = 3;
ASSERT_EQUAL(x, y);
}
#endif

72
src/util/test.h Normal file
View File

@@ -0,0 +1,72 @@
#ifndef TEST_H
#define TEST_H
#include "bool.h"
#include "print.h"
typedef void Test(Status *test_result_);
void register_test_(Test *test, const char *name);
#define TEST(name) \
void test_##name(Status *test_result_); \
__attribute__((constructor)) void test_register_##name() \
{ \
register_test_(test_##name, #name); \
} \
void test_##name(Status *test_result_)
void run_all_tests(bool verbose);
#define FAIL_TEST_ \
*test_result_ = FAILURE; \
return;
#define ASSERT(value) \
if (!(value)) { \
FAIL_TEST_ \
}
#define ASSERT_EQUAL(a, b) \
if (!(a == b)) { \
printf("%s:%d: Assertion failed: %s == %s: ", __FILE__, __LINE__, #a, #b); \
print(a); \
printf(" != "); \
print(b); \
FAIL_TEST_ \
}
#define ASSERT_DIFFERENT(a, b) \
if (a == b) { \
printf("%s:%d: Assertion failed: %s != %s: ", __FILE__, __LINE__, #a, #b); \
print(a); \
printf(" == "); \
print(b); \
FAIL_TEST_ \
}
#define ASSERT_STR_EQUAL(a, b) \
if (strcmp(a, b) != 0) { \
printf("%s:%d: Assertion failed: %s == %s: ", __FILE__, __LINE__, #a, #b); \
print(a); \
printf(" != "); \
print(b); \
FAIL_TEST_ \
}
#define ASSERT_STR_DIFFERENT(a, b) \
if (strcmp(a, b) == 0) { \
printf("%s:%d: Assertion failed: %s != %s: ", __FILE__, __LINE__, #a, #b); \
print(a); \
printf(" == "); \
print(b); \
FAIL_TEST_ \
}
#define ASSERT_TRUE(value) ASSERT_EQUAL(value, 1)
#define ASSERT_FALSE(value) ASSERT_EQUAL(value, 0)
#define ASSERT_NULL(value) ASSERT_EQUAL(value, NULL)
#define ASSERT_NON_NULL(value) ASSERT_DIFFERENT(value, NULL)
#endif

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