Compare commits

...

221 Commits
v15.3 ... gtk-3

Author SHA1 Message Date
Chris Lee
97a8eade1d attempt to port to gtk3 2021-01-04 21:55:12 +01:00
Chris Lee
06bcb3f2da attempt to fix #786 2021-01-02 23:22:22 +01:00
Chris
03b86f5f2c Merge branch 'big_icon_crash' into 'master'
Fix crashing when a window icon is large

See merge request o9000/tint2!35
2020-12-24 18:14:26 +00:00
Chris
b56147efb7 Merge branch 'executor-example-memusage' into 'master'
Enhanced example for memusage executor

Closes #787

See merge request o9000/tint2!38
2020-12-24 18:10:22 +00:00
Nikita Zlobin
ca3588cab0 Improve network bandwidth executor example too (stdbuf -oL)
If memusage example got stdbuf -oL awk, why further examples
can't use same?
2020-12-24 00:25:41 +05:00
Nikita Zlobin
795302fbaf More reasonable comment about stdbuf -oL 2020-12-24 00:22:48 +05:00
Nikita Zlobin
60ee870ae5 Oops, avoid gawk coprocess extension 2020-12-24 00:16:00 +05:00
Nikita Zlobin
975149642f Use numfmt to get back human output in memory usage executor 2020-12-05 23:48:33 +05:00
Nikita Zlobin
bba945b3b9 Enhanced example for memory usage executor, sync for all docs
It seems, html help already had one free instance with -s option for persistance,
though still used inner non-persistant chain for humanized output.
2020-11-30 13:00:21 +05:00
Chris Lee
5b3f9c4662 fix help message 2020-06-27 13:55:55 +00:00
Chris Lee
3bdb0e03f2 #643 Hide/unhide programatically 2020-06-27 12:02:00 +00:00
santouits
27a8ea013e Fix crashing when a window icon is large
If an icon of a window is large tint2 crashes with segmentation fault, I guess because of stack overflow. This changes the allocation of icon_data from stack to heap.
2019-10-29 21:22:23 +02:00
Chris Lee
78313502d3 Release 16.7 2019-07-14 23:15:40 -07:00
Chris Lee
3eae1ba912 fix release script 2019-07-14 23:15:01 -07:00
Chris Lee
22ce7c5427 Remove debug code from release mode 2019-07-12 22:47:07 -07:00
Chris Lee
784edaccd2 Expand leading tilde in icon paths (fixes #743) 2019-06-02 22:08:20 +02:00
Chris Lee
b9c313cd18 add script to compile dependencies 2019-04-28 19:24:51 +02:00
Chris Lee
10723b1db5 workaround broken battery blink with intel gpu 2019-03-09 12:51:33 +01:00
Chris Lee
89c02d3912 use simpler battery blink method 2019-03-09 12:33:52 +01:00
Chris Lee
da75999832 add env var to debug battery blink 2019-03-09 12:29:31 +01:00
Chris Lee
bff1a337f3 Revert "do not draw text with cairo directly on xlib pixmap (workaround for intel graphics bug, see emacs implementation) (issue #731, issue #732)"
This reverts commit e1211a929f.
2019-03-09 12:23:41 +01:00
Chris Lee
2f372364f0 disable glib slice allocator 2019-03-04 03:42:53 +01:00
Chris Lee
a83e057414 update changelog 2019-03-03 23:16:33 +01:00
Chris Lee
7f070b4f45 Battery percentage without % sign (#730) 2019-03-03 23:13:40 +01:00
Chris Lee
a859727ff3 more detailed logging for thumbnails 2019-03-03 22:48:47 +01:00
Chris Lee
6c36f79aae enable shared memory method of getting window screenshot 2019-03-03 01:46:59 +01:00
Chris Lee
d682756012 safety measures in thumbnail code 2019-03-03 01:40:12 +01:00
Chris Lee
b6c19da36c update authors and changelog 2019-02-24 23:13:17 +01:00
Chris Lee
372b51a4fa use named constants in struts 2019-02-24 23:09:55 +01:00
Chris
86dcaa7a83 Merge branch 'master' into 'master'
Allow pivoting STRUTs for panels in the middle of virtual screens on some WMs

See merge request o9000/tint2!31
2019-02-24 21:58:35 +00:00
Chris Billington
2b0f6a9869 Allow pivoting STRUTs for panels in the middle of virtual screens on some WMs 2019-02-24 21:58:35 +00:00
Chris Lee
4dafea185f thumbnails: do not crash if window size is 0x0 2019-02-23 22:49:58 +01:00
Chris Lee
77890d463a blink battery between two colors (issue #723) 2019-02-23 20:06:47 +01:00
Chris Lee
e1211a929f do not draw text with cairo directly on xlib pixmap (workaround for intel graphics bug, see emacs implementation) 2019-02-23 20:06:06 +01:00
Chris Lee
00f0bbd354 blink battery when low (issue #723) 2019-02-20 23:38:17 +01:00
Chris Lee
15d2570a3e Silence warnings 2018-12-31 18:23:41 +01:00
Chris Lee
c76f056746 Fix markup drawing when font shadow enabled (fixes issue #709) 2018-12-31 18:22:50 +01:00
Chris Lee
cacd4b8dad Fix: Bug: Clock Only Updates Every Minute With Format %s (issue #724) 2018-12-27 08:01:11 +01:00
Chris Lee
b539c0a1c0 fix crash if bad config path given #719 2018-09-25 19:55:55 +00:00
Chris Lee
970c597796 #716 fix executor spacing 2018-09-15 05:46:14 +00:00
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
104 changed files with 12475 additions and 4858 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>
@@ -31,6 +32,9 @@ Contributors:
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
Chris Billington (https://gitlab.com/chrisjbillington) : panel struts pivoting
Translations:
Bosnian:
@@ -45,3 +49,5 @@ Translations:
Daniel Napora <napcok@gmail.com>
Serbian:
Dino Duratović <dinomol@mail.com>
Spanish:
Vic <vicmz@yandex.com>

View File

@@ -25,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 )
@@ -62,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 )
@@ -116,13 +127,14 @@ include_directories( ${PROJECT_BINARY_DIR}
set( SOURCES src/config.c
src/panel.c
src/server.c
src/util/server.c
src/main.c
src/init.c
src/signals.c
src/tracing.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
@@ -140,13 +152,17 @@ 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 )
@@ -233,7 +249,7 @@ else()
SET(TRACING_L_FLAGS "")
endif()
add_custom_target( version ALL "${PROJECT_SOURCE_DIR}/get_version.sh" -- "\"${PROJECT_SOURCE_DIR}/\"" )
add_custom_target( version ALL "${PROJECT_SOURCE_DIR}/get_version.sh" )
link_directories( ${X11_LIBRARY_DIRS}
${PANGOCAIRO_LIBRARY_DIRS}
@@ -267,7 +283,7 @@ 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} ${TRACING_C_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 )

View File

@@ -1,3 +1,61 @@
2019-07-14 16.7
- Fixes:
- Fix spacing around icons in executor without text in vertical panels (issue #716)
- Fix Bug: Clock Only Updates Every Minute With Format %s (issue #724)
- Fix markup drawing when font shadow enabled (issue #709)
- Struts pivoting: workaround for panel positioning between monitors in mutter (Chris Billington)
- Enhancements:
- Blink battery when discharging and low (issue #723)
- Add support for battery percentage without % sign (issue #730)
- Expand leading ~ in icon paths
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)
@@ -965,3 +1023,11 @@ released tint-0.2
.
.
.
.
.
.
.
.
.
.
.

View File

@@ -1,5 +1,5 @@
# Latest stable release: 15.3
Changes: https://gitlab.com/o9000/tint2/blob/15.3/ChangeLog
# Latest stable release: 16.7
Changes: https://gitlab.com/o9000/tint2/blob/16.7/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 15.3
git checkout 16.7
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?

View File

@@ -270,8 +270,10 @@ 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
<p>You can define as many backgrounds as you want. For example, the following config defines two backgrounds:</p><div class="highlight"><pre class="highlight plaintext"><code>rounded = 1
border_width = 0
background_color = #282828 100
border_color = #000000 0
@@ -280,16 +282,14 @@ rounded = 1
border_width = 0
background_color = #f6b655 90
border_color = #cccccc 40
</code></pre>
<p>tint2 automatically identifies each background with a number starting from 1 (1, 2, ...).
Afterwards, you can apply a background to objects (panel, taskbar, task, clock, systray) using the background id, for example:</p><pre class="highlight plaintext"><code>panel_background_id = 1
</code></pre></div><p>tint2 automatically identifies each background with a number starting from 1 (1, 2, ...).
Afterwards, you can apply a background to objects (panel, taskbar, task, clock, systray) using the background id, for example:</p><div class="highlight"><pre class="highlight plaintext"><code>panel_background_id = 1
taskbar_background_id = 0
task_background_id = 0
task_active_background_id = 2
systray_background_id = 0
clock_background_id = 0
</code></pre>
<p>Identifier 0 refers to a special background which is fully transparent, identifier 1 applies the first background defined in the config file etc.</p><h3 id="gradients">Gradients<a name="gradients" href="#gradients" class="md2man-permalink" title="permalink"></a></h3><p>(Available since 0.13.0)</p><p>Backgrounds also allow specifying gradient layers
</code></pre></div><p>Identifier 0 refers to a special background which is fully transparent, identifier 1 applies the first background defined in the config file etc.</p><h3 id="gradients">Gradients<a name="gradients" href="#gradients" class="md2man-permalink" title="permalink"></a></h3><p>(Available since 0.13.0)</p><p>Backgrounds also allow specifying gradient layers
that are drawn on top of the solid color background.</p><p>First the user must define one or more gradients in the config file,
each starting with <code>gradient = TYPE</code>. These must be added before backgrounds.</p><p>Then gradients can be added by index to backgrounds,
using the <code>gradient_id = INDEX</code>, <code>gradient_id_hover = INDEX</code> and
@@ -298,21 +298,17 @@ the gradient index, starting from 1.</p><h4 id="gradient-types">Gradient types<a
* vertical gradients: top-to-bottom;
* horizontal gradients: left-to-right;
* radial gradients: center-to-corners.</p><p>The user must specify the start and end colors, and can optionally add extra color stops in between
using the <code>color_stop</code> option, as explained below.</p><h5 id="vertical-gradient-with-color-varying-from-the-top-edge-to-the-bottom-edge-two-colors">Vertical gradient, with color varying from the top edge to the bottom edge, two colors<a name="vertical-gradient-with-color-varying-from-the-top-edge-to-the-bottom-edge-two-colors" href="#vertical-gradient-with-color-varying-from-the-top-edge-to-the-bottom-edge-two-colors" class="md2man-permalink" title="permalink"></a></h5><pre class="highlight plaintext"><code>gradient = vertical
using the <code>color_stop</code> option, as explained below.</p><h5 id="vertical-gradient-with-color-varying-from-the-top-edge-to-the-bottom-edge-two-colors">Vertical gradient, with color varying from the top edge to the bottom edge, two colors<a name="vertical-gradient-with-color-varying-from-the-top-edge-to-the-bottom-edge-two-colors" href="#vertical-gradient-with-color-varying-from-the-top-edge-to-the-bottom-edge-two-colors" class="md2man-permalink" title="permalink"></a></h5><div class="highlight"><pre class="highlight plaintext"><code>gradient = vertical
start_color = #rrggbb opacity
end_color = #rrggbb opacity
</code></pre>
<h5 id="horizontal-gradient-with-color-varying-from-the-left-edge-to-the-right-edge-two-colors">Horizontal gradient, with color varying from the left edge to the right edge, two colors<a name="horizontal-gradient-with-color-varying-from-the-left-edge-to-the-right-edge-two-colors" href="#horizontal-gradient-with-color-varying-from-the-left-edge-to-the-right-edge-two-colors" class="md2man-permalink" title="permalink"></a></h5><pre class="highlight plaintext"><code>gradient = horizontal
</code></pre></div><h5 id="horizontal-gradient-with-color-varying-from-the-left-edge-to-the-right-edge-two-colors">Horizontal gradient, with color varying from the left edge to the right edge, two colors<a name="horizontal-gradient-with-color-varying-from-the-left-edge-to-the-right-edge-two-colors" href="#horizontal-gradient-with-color-varying-from-the-left-edge-to-the-right-edge-two-colors" class="md2man-permalink" title="permalink"></a></h5><div class="highlight"><pre class="highlight plaintext"><code>gradient = horizontal
start_color = #rrggbb opacity
end_color = #rrggbb opacity
</code></pre>
<h5 id="radial-gradient-with-color-varying-from-the-center-to-the-corner-two-colors">Radial gradient, with color varying from the center to the corner, two colors:<a name="radial-gradient-with-color-varying-from-the-center-to-the-corner-two-colors" href="#radial-gradient-with-color-varying-from-the-center-to-the-corner-two-colors" class="md2man-permalink" title="permalink"></a></h5><pre class="highlight plaintext"><code>gradient = radial
</code></pre></div><h5 id="radial-gradient-with-color-varying-from-the-center-to-the-corner-two-colors">Radial gradient, with color varying from the center to the corner, two colors:<a name="radial-gradient-with-color-varying-from-the-center-to-the-corner-two-colors" href="#radial-gradient-with-color-varying-from-the-center-to-the-corner-two-colors" class="md2man-permalink" title="permalink"></a></h5><div class="highlight"><pre class="highlight plaintext"><code>gradient = radial
start_color = #rrggbb opacity
end_color = #rrggbb opacity
</code></pre>
<h5 id="adding-extra-color-stops-0-and-100-remain-fixed-more-colors-at-x-between-the-start-and-end-control-points">Adding extra color stops (0% and 100% remain fixed, more colors at x% between the start and end control points)<a name="adding-extra-color-stops-0-and-100-remain-fixed-more-colors-at-x-between-the-start-and-end-control-points" href="#adding-extra-color-stops-0-and-100-remain-fixed-more-colors-at-x-between-the-start-and-end-control-points" class="md2man-permalink" title="permalink"></a></h5><pre class="highlight plaintext"><code>color_stop = percentage #rrggbb opacity
</code></pre>
<h4 id="gradient-examples">Gradient examples<a name="gradient-examples" href="#gradient-examples" class="md2man-permalink" title="permalink"></a></h4><pre class="highlight plaintext"><code># Gradient 1: thin film effect
</code></pre></div><h5 id="adding-extra-color-stops-0-and-100-remain-fixed-more-colors-at-x-between-the-start-and-end-control-points">Adding extra color stops (0% and 100% remain fixed, more colors at x% between the start and end control points)<a name="adding-extra-color-stops-0-and-100-remain-fixed-more-colors-at-x-between-the-start-and-end-control-points" href="#adding-extra-color-stops-0-and-100-remain-fixed-more-colors-at-x-between-the-start-and-end-control-points" class="md2man-permalink" title="permalink"></a></h5><div class="highlight"><pre class="highlight plaintext"><code>color_stop = percentage #rrggbb opacity
</code></pre></div><h4 id="gradient-examples">Gradient examples<a name="gradient-examples" href="#gradient-examples" class="md2man-permalink" title="permalink"></a></h4><div class="highlight"><pre class="highlight plaintext"><code># Gradient 1: thin film effect
gradient = horizontal
start_color = #111122 30
end_color = #112211 30
@@ -348,8 +344,7 @@ gradient_id_hover = 4
gradient_id_pressed = 2
[...]
</code></pre>
<h3 id="panel">Panel<a name="panel" href="#panel" class="md2man-permalink" title="permalink"></a></h3>
</code></pre></div><h3 id="panel">Panel<a name="panel" href="#panel" class="md2man-permalink" title="permalink"></a></h3>
<ul>
<li><p><code>panel_items = LTSBC</code> defines the items tint2 will show and the order of those items. Each letter refers to an item, defined as:</p>
<ul>
@@ -384,11 +379,12 @@ 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:
<div class="highlight"><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
</code></pre>
</code></pre></div>
<ul>
<li><p><code>panel_shrink = boolean (0 or 1)</code> : If set to 1, the panel will shrink to a compact size dynamically. <em>(since 0.13)</em></p></li>
<li><p><code>panel_margin = horizontal_margin vertical_margin</code> : The margins define the distance between the panel and the horizontal/vertical monitor edge. Use <code>0</code> to obtain a panel with the same size as the edge of the monitor (no margin).</p></li>
@@ -403,8 +399,9 @@ panel_size = 94% 30
<li><p><code>panel_background_id = integer</code> : Which background to use for the panel.</p></li>
<li><p><code>wm_menu = boolean (0 or 1)</code> : Defines if tint2 forwards unhandled mouse events to your window manager. Useful for window managers such as openbox, which display the start menu if you right click on the desktop.</p></li>
<li><p><code>panel_dock = boolean (0 or 1)</code> : Defines if tint2 is placed into the window manager&#39;s dock. For the openbox window manager it is advised to also use a modifier for the moveButton option, otherwise the mouse click is not forwarded to tint2 (in ~/.config/openbox/rc.xml).</p></li>
<li><p><code>panel_pivot_struts = boolean (0 or 1)</code> : Defines if tint2 lies to the window manager about its orientation (horizontal vs vertical) when requesting reserved space with STRUTs (see <code>strut_policy</code> below). On some window managers, this allows placing a panel in the middle of the virtual screen, e.g. on the bottom edge of the top monitor in a vertical dual-monitor setup. </p></li>
<li><p><code>panel_layer = bottom/normal/top</code> : Places tint2 into the bottom/normal/top layer. This is helpful for specifying if the panel can be covered by other windows or not. The default is the bottom layer, but with real transparency normal or top layer may be a nice alternative.</p></li>
<li><p><code>strut_policy = follow_size/minimum/none</code> : STRUTs are used by the window manager to decide the size of maximized windows. Note: on multi-monitor (Xinerama) setups, the panel must be placed at the edge (not in the middle) of the virtual screen for this to work correctly.</p>
<li><p><code>strut_policy = follow_size/minimum/none</code> : STRUTs are used by the window manager to decide the size of maximized windows. Note: on multi-monitor (Xinerama) setups, the panel generally must be placed at the edge (not in the middle) of the virtual screen for this to work correctly (though on some window managers, setting <code>panel_pivot_struts</code> may work around this limitation). </p>
<ul>
<li><code>follow_size</code> means that the maximized windows always resize to have a common edge with tint2.</li>
<li><code>minimum</code> means that the maximized windows always expand to have a common edge with the hidden panel. This is useful if the <code>autohide</code> option is enabled.</li>
@@ -462,6 +459,7 @@ panel_size = 94% 30
<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>
@@ -481,6 +479,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>
@@ -566,7 +566,7 @@ 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>
@@ -578,6 +578,7 @@ panel_size = 94% 30
<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>
<li>%P: Percentage. Without 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>
@@ -601,7 +602,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>
@@ -614,15 +615,13 @@ panel_size = 94% 30
<li><p><code>execp_uwheel_command = text</code> : Command to execute on wheel scroll up. If not defined, <code>execp_command</code> is executed immediately, unless it is currently running. <em>(since 0.12.4)</em></p></li>
<li><p><code>execp_dwheel_command = text</code> : Command to execute on wheel scroll down. If not defined, <code>execp_command</code> is executed immediately, unless it is currently running. <em>(since 0.12.4)</em></p></li>
</ul>
<h4 id="executor-samples">Executor samples<a name="executor-samples" href="#executor-samples" class="md2man-permalink" title="permalink"></a></h4><h5 id="print-the-hostname">Print the hostname<a name="print-the-hostname" href="#print-the-hostname" class="md2man-permalink" title="permalink"></a></h5><pre class="highlight plaintext"><code>execp = new
<h4 id="executor-samples">Executor samples<a name="executor-samples" href="#executor-samples" class="md2man-permalink" title="permalink"></a></h4><h5 id="print-the-hostname">Print the hostname<a name="print-the-hostname" href="#print-the-hostname" class="md2man-permalink" title="permalink"></a></h5><div class="highlight"><pre class="highlight plaintext"><code>execp = new
execp_command = hostname
execp_interval = 0
</code></pre>
<h5 id="print-disk-usage-for-the-root-partition-every-10-seconds">Print disk usage for the root partition every 10 seconds<a name="print-disk-usage-for-the-root-partition-every-10-seconds" href="#print-disk-usage-for-the-root-partition-every-10-seconds" class="md2man-permalink" title="permalink"></a></h5><pre class="highlight plaintext"><code>execp = new
</code></pre></div><h5 id="print-disk-usage-for-the-root-partition-every-10-seconds">Print disk usage for the root partition every 10 seconds<a name="print-disk-usage-for-the-root-partition-every-10-seconds" href="#print-disk-usage-for-the-root-partition-every-10-seconds" class="md2man-permalink" title="permalink"></a></h5><div class="highlight"><pre class="highlight plaintext"><code>execp = new
execp_command = df -h | awk '/\/$/ { print $6 ": " $2 " " $5}'
execp_interval = 10
</code></pre>
<h5 id="button-with-icon-and-rich-text-executes-command-when-clicked">Button with icon and rich text, executes command when clicked<a name="button-with-icon-and-rich-text-executes-command-when-clicked" href="#button-with-icon-and-rich-text-executes-command-when-clicked" class="md2man-permalink" title="permalink"></a></h5><pre class="highlight plaintext"><code>execp = new
</code></pre></div><h5 id="button-with-icon-and-rich-text-executes-command-when-clicked">Button with icon and rich text, executes command when clicked<a name="button-with-icon-and-rich-text-executes-command-when-clicked" href="#button-with-icon-and-rich-text-executes-command-when-clicked" class="md2man-permalink" title="permalink"></a></h5><div class="highlight"><pre class="highlight plaintext"><code>execp = new
execp_command = echo /usr/share/icons/elementary-xfce/emblems/24/emblem-colors-blue.png; echo '&lt;span foreground="#7f7"&gt;Click&lt;/span&gt; &lt;span foreground="#77f"&gt;me&lt;/span&gt; &lt;span foreground="#f77"&gt;pls&lt;/span&gt;'
execp_has_icon = 1
execp_interval = 0
@@ -634,36 +633,30 @@ execp_padding = 2 0
execp_tooltip = I will tell you a secret...
execp_lclick_command = zenity --info "--text=$(uname -sr)"
execp_background_id = 2
</code></pre>
<h5 id="desktop-pager-with-text">Desktop pager with text<a name="desktop-pager-with-text" href="#desktop-pager-with-text" class="md2man-permalink" title="permalink"></a></h5><pre class="highlight plaintext"><code>execp = new
</code></pre></div><h5 id="desktop-pager-with-text">Desktop pager with text<a name="desktop-pager-with-text" href="#desktop-pager-with-text" class="md2man-permalink" title="permalink"></a></h5><div class="highlight"><pre class="highlight plaintext"><code>execp = new
execp_command = xprop -root -spy | awk '/^_NET_CURRENT_DESKTOP/ { print "Workspace " ($3 + 1) ; fflush(); }'
execp_interval = 1
execp_continuous = 1
</code></pre>
<h5 id="desktop-pager-with-icon">Desktop pager with icon<a name="desktop-pager-with-icon" href="#desktop-pager-with-icon" class="md2man-permalink" title="permalink"></a></h5><pre class="highlight plaintext"><code>execp_command = xprop -root -spy | awk -v home="$HOME" '/^_NET_CURRENT_DESKTOP/ { print home "/.config/myPager/" ($3 + 1) ".png\n" ; fflush(); }'
</code></pre></div><h5 id="desktop-pager-with-icon">Desktop pager with icon<a name="desktop-pager-with-icon" href="#desktop-pager-with-icon" class="md2man-permalink" title="permalink"></a></h5><div class="highlight"><pre class="highlight plaintext"><code>execp_command = xprop -root -spy | awk -v home="$HOME" '/^_NET_CURRENT_DESKTOP/ { print home "/.config/myPager/" ($3 + 1) ".png\n" ; fflush(); }'
execp_interval = 1
execp_has_icon = 1
execp_cache_icon = 1
execp_continuous = 2
</code></pre>
<h5 id="round-trip-time-to-the-gateway-refreshed-every-second">Round-trip time to the gateway, refreshed every second<a name="round-trip-time-to-the-gateway-refreshed-every-second" href="#round-trip-time-to-the-gateway-refreshed-every-second" class="md2man-permalink" title="permalink"></a></h5><pre class="highlight plaintext"><code>execp = new
</code></pre></div><h5 id="round-trip-time-to-the-gateway-refreshed-every-second">Round-trip time to the gateway, refreshed every second<a name="round-trip-time-to-the-gateway-refreshed-every-second" href="#round-trip-time-to-the-gateway-refreshed-every-second" class="md2man-permalink" title="permalink"></a></h5><div class="highlight"><pre class="highlight plaintext"><code>execp = new
execp_command = ping -i 1 -c 1 -W 1 -O -D -n $(ip route | grep default | grep via | grep -o '[0-9]*\.[0-9]*\.[0-9]*\.[0-9]*') | awk '/no/ { print "&lt;span foreground=\"#faa\"&gt;timeout&lt;/span&gt;"; fflush(); }; /time=/ { gsub(/time=/, "", $8); printf "&lt;span foreground=\"#7af\"&gt;%3.0f %s&lt;/span&gt;\n", $8, $9; fflush(); } '
execp_continuous = 0
execp_interval = 1
execp_markup = 1
</code></pre>
<h5 id="memory-usage">Memory usage<a name="memory-usage" href="#memory-usage" class="md2man-permalink" title="permalink"></a></h5><pre class="highlight plaintext"><code>execp = new
</code></pre></div><h5 id="memory-usage">Memory usage<a name="memory-usage" href="#memory-usage" class="md2man-permalink" title="permalink"></a></h5><div class="highlight"><pre class="highlight plaintext"><code>execp = new
execp_command = free | awk '/^-/ { printf "Mem: '$(free -h | awk '/^Mem:/ { print $2 }')' %.0f%%\n", 100*$3/($3+$4); fflush(stdout) }'
execp_interval = 5
execp_continuous = 0
</code></pre>
<h5 id="network-load">Network load<a name="network-load" href="#network-load" class="md2man-permalink" title="permalink"></a></h5><pre class="highlight plaintext"><code># Note the use of "stdbuf -oL" to force the program to flush the output line by line.
</code></pre></div><h5 id="network-load">Network load<a name="network-load" href="#network-load" class="md2man-permalink" title="permalink"></a></h5><div class="highlight"><pre class="highlight plaintext"><code># Note the use of "stdbuf -oL" to force the program to flush the output line by line.
execp = new
execp_command = stdbuf -oL bwm-ng -o csv -t 1000 | awk -F ';' '/total/ { printf "Net: %.0f Mb/s\n", ($5*8/1.0e6) }; fflush(stdout)'
execp_continuous = 1
execp_interval = 1
</code></pre>
<h3 id="button">Button<a name="button" href="#button" class="md2man-permalink" title="permalink"></a></h3>
</code></pre></div><h3 id="button">Button<a name="button" href="#button" class="md2man-permalink" title="permalink"></a></h3>
<ul>
<li><p><code>button = new</code> : Begins the configuration of a new button. Multiple such plugins are supported; just use multiple <code>P</code>s in <code>panel_items</code>. <em>(since 0.14)</em></p></li>
<li><p><code>button_icon = text</code> : Name or path of icon (or empty). <em>(since 0.14)</em></p></li>
@@ -695,6 +688,7 @@ It is based on ttm, originally written by Pål Staurland <a href="mailto:staura@
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>
</body>
</html>

View File

@@ -199,19 +199,17 @@ pre {
</style>
</head>
<body>
<h1 id="latest-stable-release-15-3"><span class="md2man-title">Latest</span> <span class="md2man-section">stable</span> <span class="md2man-date">release:</span> <span class="md2man-source">15.3</span><a name="latest-stable-release-15-3" href="#latest-stable-release-15-3" class="md2man-permalink" title="permalink"></a></h1><p>Changes: <a href="https://gitlab.com/o9000/tint2/blob/15.3/ChangeLog">https://gitlab.com/o9000/tint2/blob/15.3/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-7"><span class="md2man-title">Latest</span> <span class="md2man-section">stable</span> <span class="md2man-date">release:</span> <span class="md2man-source">16.7</span><a name="latest-stable-release-16-7" href="#latest-stable-release-16-7" class="md2man-permalink" title="permalink"></a></h1><p>Changes: <a href="https://gitlab.com/o9000/tint2/blob/16.7/ChangeLog">https://gitlab.com/o9000/tint2/blob/16.7/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><div class="highlight"><pre class="highlight plaintext"><code>git clone https://gitlab.com/o9000/tint2.git
cd tint2
git checkout 15.3
git checkout 16.7
mkdir build
cd build
cmake ..
make -j4
</code></pre>
<p>To install, run (as root):</p><pre class="highlight plaintext"><code>make install
</code></pre></div><p>To install, run (as root):</p><div class="highlight"><pre class="highlight plaintext"><code>make install
update-icon-caches /usr/local/share/icons/hicolor
update-mime-database /usr/local/share/mime
</code></pre>
<p>And then you can run the panel <code>tint2</code> and the configuration program <code>tint2conf</code>.</p><p>Please report any problems to <a href="https://gitlab.com/o9000/tint2/issues">https://gitlab.com/o9000/tint2/issues</a>. Your feedback is much appreciated.</p><p>P.S. GitLab is now the official location of the tint2 project, migrated from Google Code, which is shutting down. In case you are wondering why not GitHub, BitBucket etc., we chose GitLab because it is open source, it is mature and works well, looks cool and has a very nice team.</p><h1 id="what-is-tint2">What is tint2?<a name="what-is-tint2" href="#what-is-tint2" class="md2man-permalink" title="permalink"></a></h1><p>tint2 is a simple panel/taskbar made for modern X window managers. It was specifically made for Openbox but it should also work with other window managers (GNOME, KDE, XFCE etc.). It is based on ttm <a href="https://code.google.com/p/ttm/">https://code.google.com/p/ttm/</a>.</p><h1 id="features">Features<a name="features" href="#features" class="md2man-permalink" title="permalink"></a></h1>
</code></pre></div><p>And then you can run the panel <code>tint2</code> and the configuration program <code>tint2conf</code>.</p><p>Please report any problems to <a href="https://gitlab.com/o9000/tint2/issues">https://gitlab.com/o9000/tint2/issues</a>. Your feedback is much appreciated.</p><p>P.S. GitLab is now the official location of the tint2 project, migrated from Google Code, which is shutting down. In case you are wondering why not GitHub, BitBucket etc., we chose GitLab because it is open source, it is mature and works well, looks cool and has a very nice team.</p><h1 id="what-is-tint2">What is tint2?<a name="what-is-tint2" href="#what-is-tint2" class="md2man-permalink" title="permalink"></a></h1><p>tint2 is a simple panel/taskbar made for modern X window managers. It was specifically made for Openbox but it should also work with other window managers (GNOME, KDE, XFCE etc.). It is based on ttm <a href="https://code.google.com/p/ttm/">https://code.google.com/p/ttm/</a>.</p><h1 id="features">Features<a name="features" href="#features" class="md2man-permalink" title="permalink"></a></h1>
<ul>
<li>Panel with taskbar, system tray, clock and launcher icons;</li>
<li>Easy to customize: color/transparency on fonts, icons, borders and backgrounds;</li>
@@ -239,9 +237,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>

View File

@@ -1,4 +1,4 @@
.TH TINT2 1 "2017\-11\-05" 15.3
.TH TINT2 1 "2019\-07\-14" 16.7
.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:
@@ -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
@@ -371,9 +379,11 @@ panel_size = 94% 30
.IP \(bu 2
\fB\fCpanel_dock = boolean (0 or 1)\fR : Defines if tint2 is placed into the window manager's dock. For the openbox window manager it is advised to also use a modifier for the moveButton option, otherwise the mouse click is not forwarded to tint2 (in ~/.config/openbox/rc.xml).
.IP \(bu 2
\fB\fCpanel_pivot_struts = boolean (0 or 1)\fR : Defines if tint2 lies to the window manager about its orientation (horizontal vs vertical) when requesting reserved space with STRUTs (see \fB\fCstrut_policy\fR below). On some window managers, this allows placing a panel in the middle of the virtual screen, e.g. on the bottom edge of the top monitor in a vertical dual\-monitor setup.
.IP \(bu 2
\fB\fCpanel_layer = bottom/normal/top\fR : Places tint2 into the bottom/normal/top layer. This is helpful for specifying if the panel can be covered by other windows or not. The default is the bottom layer, but with real transparency normal or top layer may be a nice alternative.
.IP \(bu 2
\fB\fCstrut_policy = follow_size/minimum/none\fR : STRUTs are used by the window manager to decide the size of maximized windows. Note: on multi\-monitor (Xinerama) setups, the panel must be placed at the edge (not in the middle) of the virtual screen for this to work correctly.
\fB\fCstrut_policy = follow_size/minimum/none\fR : STRUTs are used by the window manager to decide the size of maximized windows. Note: on multi\-monitor (Xinerama) setups, the panel generally must be placed at the edge (not in the middle) of the virtual screen for this to work correctly (though on some window managers, setting \fB\fCpanel_pivot_struts\fR may work around this limitation).
.RS
.IP \(bu 2
\fB\fCfollow_size\fR means that the maximized windows always resize to have a common edge with tint2.
@@ -472,6 +482,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
@@ -508,6 +520,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
@@ -651,7 +667,7 @@ 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
@@ -673,6 +689,8 @@ To hide the clock, comment \fB\fCtime1_format\fR and \fB\fCtime2_format\fR\&.
%t: Time left. Shows "hrs:mins" when charging/discharging, or "Ful\[rs]" when full.
.IP \(bu 2
%p: Percentage. Includes the % sign.
.IP \(bu 2
%P: Percentage. Without the % sign.
.RE
.IP \(bu 2
\fB\fCbat2_format = FORMAT_STRING\fR : Format for battery line 2. Default: %t. \fI(since 1.0)\fP
@@ -716,7 +734,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
@@ -813,19 +831,19 @@ execp_markup = 1
.PP
.RS
.nf
# Note the use of "stdbuf \-oL" to force the program to flush the output line by line.
execp = new
execp_command = free | awk '/^\-/ { printf "Mem: '$(free \-h | awk '/^Mem:/ { print $2 }')' %.0f%%\\n", 100*$3/($3+$4); fflush(stdout) }'
execp_interval = 5
execp_continuous = 0
execp_command = free -b -s1 | stdbuf -oL awk '/^Mem:/ { printf "Mem: %s %.0f%%\n", $2, 100 * ($2 - $7) / $2 }' | stdbuf -oL numfmt --to=iec-i --field=2 -d' '
execp_interval = 1
execp_continuous = 1
.fi
.RE
.SS Network load
.PP
.RS
.nf
# Note the use of "stdbuf \-oL" to force the program to flush the output line by line.
execp = new
execp_command = stdbuf \-oL bwm\-ng \-o csv \-t 1000 | awk \-F ';' '/total/ { printf "Net: %.0f Mb/s\\n", ($5*8/1.0e6) }; fflush(stdout)'
execp_command = stdbuf \-oL bwm\-ng \-o csv \-t 1000 | stdbuf -oL awk \-F ';' '/total/ { printf "Net: %.0f Mb/s\\n", ($5*8/1.0e6) }'
execp_continuous = 1
execp_interval = 1
.fi
@@ -896,3 +914,4 @@ and the wiki page at \[la]https://gitlab.com/o9000/tint2/wikis/home\[ra]\&.
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

@@ -372,14 +372,14 @@ execp_continuous = 1
execp_interval = 1
execp_markup = 1
</code></pre>
<h5 id="memory-usage">Memory usage<a name="memory-usage" href="#memory-usage" class="md2man-permalink" title="permalink"></a></h5><pre class="highlight plaintext"><code>execp = new
execp_command = free -s 2 | awk '/^-/ { printf "Mem: '$(free -h | awk '/^Mem:/ { print $2 }')' %.0f%\n", 100*$3/($3+$4); fflush(stdout) }'
<h5 id="memory-usage">Memory usage<a name="memory-usage" href="#memory-usage" class="md2man-permalink" title="permalink"></a></h5><pre class="highlight plaintext"><code># Note the use of "stdbuf -oL" to force the program to flush the output line by line.
execp = new
execp_command = free -b -s1 | stdbuf -oL awk '/^Mem:/ { printf "Mem: %s %.0f%%\n", $2, 100 * ($2 - $7) / $2 }' | stdbuf -oL numfmt --to=iec-i --field=2 -d' '
execp_interval = 1
execp_continuous = 1
</code></pre>
<h5 id="network-load">Network load<a name="network-load" href="#network-load" class="md2man-permalink" title="permalink"></a></h5><pre class="highlight plaintext"><code># Note the use of "stdbuf -oL" to force the program to flush the output line by line.
execp = new
execp_command = stdbuf -oL bwm-ng -o csv -t 1000 | awk -F ';' '/total/ { printf "Net: %.0f Mb/s\n", ($5*8/1.0e6) }; fflush(stdout)'
<h5 id="network-load">Network load<a name="network-load" href="#network-load" class="md2man-permalink" title="permalink"></a></h5><pre class="highlight plaintext"><code>execp = new
execp_command = stdbuf -oL bwm-ng -o csv -t 1000 | stdbuf -oL awk -F ';' '/total/ { printf "Net: %.0f Mb/s\n", ($5*8/1.0e6) }'
execp_continuous = 1
execp_interval = 1
</code></pre>

View File

@@ -1,4 +1,4 @@
# TINT2 1 "2017-11-05" 15.3
# TINT2 1 "2019-07-14" 16.7
## 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:
```
@@ -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
@@ -298,9 +306,11 @@ panel_size = 94% 30
* `panel_dock = boolean (0 or 1)` : Defines if tint2 is placed into the window manager's dock. For the openbox window manager it is advised to also use a modifier for the moveButton option, otherwise the mouse click is not forwarded to tint2 (in ~/.config/openbox/rc.xml).
* `panel_pivot_struts = boolean (0 or 1)` : Defines if tint2 lies to the window manager about its orientation (horizontal vs vertical) when requesting reserved space with STRUTs (see `strut_policy` below). On some window managers, this allows placing a panel in the middle of the virtual screen, e.g. on the bottom edge of the top monitor in a vertical dual-monitor setup.
* `panel_layer = bottom/normal/top` : Places tint2 into the bottom/normal/top layer. This is helpful for specifying if the panel can be covered by other windows or not. The default is the bottom layer, but with real transparency normal or top layer may be a nice alternative.
* `strut_policy = follow_size/minimum/none` : STRUTs are used by the window manager to decide the size of maximized windows. Note: on multi-monitor (Xinerama) setups, the panel must be placed at the edge (not in the middle) of the virtual screen for this to work correctly.
* `strut_policy = follow_size/minimum/none` : STRUTs are used by the window manager to decide the size of maximized windows. Note: on multi-monitor (Xinerama) setups, the panel generally must be placed at the edge (not in the middle) of the virtual screen for this to work correctly (though on some window managers, setting `panel_pivot_struts` may work around this limitation).
* `follow_size` means that the maximized windows always resize to have a common edge with tint2.
* `minimum` means that the maximized windows always expand to have a common edge with the hidden panel. This is useful if the `autohide` option is enabled.
* `none` means that the maximized windows use the full screen size.
@@ -377,6 +387,7 @@ panel_size = 94% 30
* `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)*
@@ -409,6 +420,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.
@@ -534,7 +549,7 @@ 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.
@@ -550,6 +565,7 @@ The action semantics:
* %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.
* %P: Percentage. Without the % sign.
* `bat2_format = FORMAT_STRING` : Format for battery line 2. Default: %t. *(since 1.0)*
@@ -591,7 +607,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)*
@@ -678,18 +694,18 @@ execp_markup = 1
##### Memory usage
```
# Note the use of "stdbuf -oL" to force the program to flush the output line by line.
execp = new
execp_command = free | awk '/^-/ { printf "Mem: '$(free -h | awk '/^Mem:/ { print $2 }')' %.0f%%\n", 100*$3/($3+$4); fflush(stdout) }'
execp_interval = 5
execp_continuous = 0
execp_command = free -b -s1 | stdbuf -oL awk '/^Mem:/ { printf "Mem: %s %.0f%%\n", $2, 100 * ($2 - $7) / $2 }' | stdbuf -oL numfmt --to=iec-i --field=2 -d' '
execp_interval = 1
execp_continuous = 1
```
##### Network load
```
# Note the use of "stdbuf -oL" to force the program to flush the output line by line.
execp = new
execp_command = stdbuf -oL bwm-ng -o csv -t 1000 | awk -F ';' '/total/ { printf "Net: %.0f Mb/s\n", ($5*8/1.0e6) }; fflush(stdout)'
execp_command = stdbuf -oL bwm-ng -o csv -t 1000 | stdbuf -oL awk -F ';' '/total/ { printf "Net: %.0f Mb/s\n", ($5*8/1.0e6) }'
execp_continuous = 1
execp_interval = 1
```
@@ -753,3 +769,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,16 +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
SCRIPT_DIR=$(dirname "$0")
VERSION=$(head -n 1 "${SCRIPT_DIR}/ChangeLog" | cut -d ' ' -f 2)
if [ "$VERSION" = "master" ]
if git describe 1>/dev/null 2>/dev/null
then
VERSION=$VERSION-$(head -n 1 "${SCRIPT_DIR}/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

@@ -55,13 +55,14 @@ def cmd(s):
def run(s):
proc = subprocess.Popen(cmd(s), shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, close_fds=True)
proc = subprocess.Popen(cmd(s), shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE, close_fds=True)
ret = proc.wait()
out = proc.communicate()[0]
out, err = proc.communicate()
for line in out.split("\n"):
debug(ansi_pinky + line + ansi_reset)
debug(ansi_pinky + "Exit code: " + str(ret))
if ret != 0:
print(err)
raise Exception("Command failed!")
return out
@@ -76,19 +77,23 @@ def get_last_version():
return tags[-1]
def inc_version(v, feature=False):
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
# v4.11 -> v4.12 or v5.0 or v4.11.1
parts = v.split(".")
while len(parts) < 2:
while len(parts) < 3:
parts.append("0")
assert len(parts) == 2
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:
del parts[-1]
parts[-1] = str(int(parts[-1]) + 1)
return ".".join([s for s in parts if s])
@@ -153,23 +158,14 @@ def update_log(path, version, date):
if __name__ == '__main__':
parser = argparse.ArgumentParser()
parser.add_argument("--feature", action="store_true")
parser.add_argument("--undo", 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.feature)
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)
@@ -194,3 +190,9 @@ if __name__ == '__main__':
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")

79
packaging/compile-deps.sh Executable file
View File

@@ -0,0 +1,79 @@
#!/bin/bash
PS4='\e[99;33m${BASH_SOURCE}:$LINENO\e[0m '
set -e
set -x
sudo apt-get install ninja-build python2.7 wget
sudo apt-get build-dep libx11-6 libxrender1 libcairo2 libglib2.0-0 libpango-1.0-0 libimlib2 librsvg2-2
export PKG_CONFIG_PATH=""
DEPS=$HOME/tint2-deps
X11=$(pkg-config --modversion x11)
XRENDER=$(pkg-config --modversion xrender)
XCOMPOSITE=$(pkg-config --modversion xcomposite)
XDAMAGE=$(pkg-config --modversion xdamage)
XEXT=$(pkg-config --modversion xext)
XRANDR=$(pkg-config --modversion xrandr)
XINERAMA=$(pkg-config --modversion xinerama)
IMLIB=$(pkg-config --modversion imlib2)
GLIB=$(pkg-config --modversion glib-2.0)
CAIRO=$(pkg-config --modversion cairo)
PANGO=$(pkg-config --modversion pango)
PIXBUF=$(pkg-config --modversion gdk-pixbuf-2.0)
RSVG=$(pkg-config --modversion librsvg-2.0)
mkdir -p $DEPS/src
function two_digits() {
echo "$@" | cut -d. -f 1-2
}
function download_and_build() {
URL="$1"
shift
CFGARGS="$@"
ARCHIVE="$(basename "$URL")"
NAME="$(echo "$ARCHIVE" | sed s/\.tar.*$//g)"
cd "$DEPS/src"
rm -rf "$ARCHIVE"
rm -rf "$NAME"
wget "$URL" -O "$ARCHIVE"
tar xf "$ARCHIVE"
cd "$NAME"
export PKG_CONFIG_PATH="$DEPS/lib/pkgconfig"
export PATH="$DEPS/bin:$PATH"
export CFLAGS="-O0 -fno-common -fno-omit-frame-pointer -rdynamic -fsanitize=address -g"
export LDFLAGS="-Wl,--no-as-needed -Wl,-z,defs -O0 -fno-common -fno-omit-frame-pointer -rdynamic -fsanitize=address -fuse-ld=gold -g -ldl -lasan"
if [[ -x ./configure ]]
then
./configure "--prefix=$DEPS" "$@"
make -j
make install
elif [[ -f meson.build ]]
then
mkdir build
cd build
meson "--prefix=$DEPS" "$@" ..
ninja install
else
echo "unknown build method"
exit 1
fi
}
download_and_build "https://www.x.org/archive/individual/lib/libX11-$X11.tar.gz" --enable-static=no
download_and_build "https://www.x.org/archive//individual/lib/libXrender-$XRENDER.tar.gz" --enable-static=no
download_and_build "https://www.x.org/archive//individual/lib/libXcomposite-$XCOMPOSITE.tar.gz" --enable-static=no
download_and_build "https://www.x.org/archive//individual/lib/libXdamage-$XDAMAGE.tar.gz" --enable-static=no
download_and_build "https://www.x.org/archive//individual/lib/libXext-$XEXT.tar.gz" --enable-static=no
download_and_build "https://www.x.org/archive//individual/lib/libXrandr-$XRANDR.tar.gz" --enable-static=no
download_and_build "https://www.x.org/archive//individual/lib/libXinerama-$XINERAMA.tar.gz" --enable-static=no
download_and_build "https://downloads.sourceforge.net/enlightenment/imlib2-$IMLIB.tar.bz2" --enable-static=no
download_and_build "https://ftp.gnome.org/pub/gnome/sources/glib/$(two_digits "$GLIB")/glib-$GLIB.tar.xz" --enable-debug=yes
download_and_build "https://ftp.gnome.org/pub/gnome/sources/gdk-pixbuf/$(two_digits "$PIXBUF")/gdk-pixbuf-$PIXBUF.tar.xz"
download_and_build "https://cairographics.org/snapshots/cairo-$CAIRO.tar.xz"
download_and_build "https://ftp.gnome.org/pub/gnome/sources/pango/$(two_digits "$PANGO")/pango-$PANGO.tar.xz"
download_and_build "https://ftp.gnome.org/pub/gnome/sources/librsvg/$(two_digits "$RSVG")/librsvg-$RSVG.tar.xz" --enable-pixbuf-loader

556
packaging/configure vendored Executable file
View File

@@ -0,0 +1,556 @@
#!/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')
print("Found", lib, "in:", ldf)
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
def remove_duplicates(ls):
result = []
for x in ls:
if x not in result:
result.append(x)
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(remove_duplicates(self.cflags)),
'lflags_' + self.name: ninja_join(remove_duplicates(self.lflags)),
'libs_' + self.name: ninja_join(remove_duplicates(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',
'gmodule-2.0',
'gio-2.0',
'glib-2.0',
'gobject-2.0',
'pangocairo',
'pango',
'cairo',
'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',
'-DUSE_REAL_MALLOC']
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',
'-Wno-shadow']
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

@@ -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 trusty xenial zesty artful
for DISTRO in trusty xenial artful bionic
do
# Cleanup from previous builds
rm -rf tint2_$VERSION-*

View File

@@ -1,5 +1,7 @@
#!/bin/bash
export PATH=/usr/local/bin:/usr/bin:/bin:/usr/local/sbin:/usr/sbin:/sbin
set -e
set -x
@@ -13,7 +15,7 @@ git reset --hard
git pull
~/tint2/packaging/version_status.py > packaging.tmp.md
timeout -k 10 600 ~/tint2/packaging/version_status.py > packaging.tmp.md
cat packaging.tmp.md > packaging.md
rm packaging.tmp.md

View File

@@ -289,7 +289,7 @@ def get_gentoo_versions():
def get_void_versions():
print >> sys.stderr, "Void ..."
template = http_download_txt("https://raw.githubusercontent.com/voidlinux/void-packages/master/srcpkgs/tint2/template")
template = http_download_txt("https://raw.githubusercontent.com/void-linux/void-packages/master/srcpkgs/tint2/template")
versions = []
version = None
maintainer = None
@@ -367,7 +367,7 @@ def get_freebsd_versions():
def get_openbsd_versions():
print >> sys.stderr, "OpenBSD ..."
makefile = http_download_txt("http://cvsweb.openbsd.org/cgi-bin/cvsweb/~checkout~/ports/x11/tint2/Makefile?rev=1.5&content-type=text/plain")
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"):

View File

@@ -41,7 +41,8 @@ struct BatteryState battery_state;
gboolean battery_enabled;
gboolean battery_tooltip_enabled;
int percentage_hide;
static timeout *battery_timeout;
static Timer battery_timer;
static Timer battery_blink_timer;
#define BATTERY_BUF_SIZE 256
static char buf_bat_line1[BATTERY_BUF_SIZE];
@@ -60,6 +61,8 @@ char *battery_rclick_command;
char *battery_uwheel_command;
char *battery_dwheel_command;
gboolean battery_found;
gboolean battery_warn;
gboolean battery_warn_red;
char *battery_sys_prefix = (char *)"";
@@ -76,7 +79,10 @@ void default_battery()
percentage_hide = 101;
battery_low_cmd_sent = FALSE;
battery_full_cmd_sent = FALSE;
battery_timeout = NULL;
INIT_TIMER(battery_timer);
INIT_TIMER(battery_blink_timer);
battery_warn = FALSE;
battery_warn_red = FALSE;
bat1_has_font = FALSE;
bat1_font_desc = NULL;
bat1_format = NULL;
@@ -127,8 +133,8 @@ 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);
destroy_timer(&battery_blink_timer);
battery_found = FALSE;
battery_os_free();
@@ -165,6 +171,7 @@ void battery_update_text(char *dest, char *format)
// %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.
// %P : Percentage left without the % sign.
if (*c == '%') {
c++;
o++; // Skip the format control character.
@@ -193,6 +200,10 @@ void battery_update_text(char *dest, char *format)
snprintf(buf, sizeof(buf), "%d%%", battery_state.percentage);
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");
@@ -226,7 +237,8 @@ void init_battery()
battery_found = battery_os_init();
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();
}
@@ -273,6 +285,7 @@ void init_battery_panel(void *p)
bat1_format = strdup("%p");
bat2_format = strdup("%t");
}
update_battery_tick(NULL);
}
void battery_init_fonts()
@@ -309,6 +322,18 @@ void battery_default_font_changed()
schedule_panel_redraw();
}
void blink_battery(void *arg)
{
if (!battery_enabled)
return;
battery_warn_red = battery_warn ? !battery_warn_red : FALSE;
for (int i = 0; i < num_panels; i++) {
if (panels[i].battery.area.on_screen) {
schedule_redraw(&panels[i].battery.area);
}
}
}
void update_battery_tick(void *arg)
{
if (!battery_enabled)
@@ -319,6 +344,7 @@ void update_battery_tick(void *arg)
gboolean old_ac_connected = battery_state.ac_connected;
int16_t old_hours = battery_state.time.hours;
int8_t old_minutes = battery_state.time.minutes;
gboolean old_warn = battery_warn;
if (!battery_found) {
init_battery();
@@ -356,6 +382,20 @@ void update_battery_tick(void *arg)
battery_full_cmd_sent = FALSE;
}
if (!battery_blink_timer.enabled_) {
if ((battery_state.percentage < battery_low_status &&
battery_state.state == BATTERY_DISCHARGING) || debug_blink) {
change_timer(&battery_blink_timer, true, 10, 1000, blink_battery, 0);
battery_warn = TRUE;
}
} else {
if (battery_state.percentage > battery_low_status ||
battery_state.state != BATTERY_DISCHARGING) {
stop_timer(&battery_blink_timer);
battery_warn = FALSE;
}
}
for (int i = 0; i < num_panels; i++) {
// Show/hide if needed
if (!battery_found) {
@@ -369,8 +409,11 @@ void update_battery_tick(void *arg)
// Redraw if needed
if (panels[i].battery.area.on_screen) {
if (old_found != battery_found || old_percentage != battery_state.percentage ||
old_hours != battery_state.time.hours || old_minutes != battery_state.time.minutes) {
old_hours != battery_state.time.hours || old_minutes != battery_state.time.minutes ||
old_warn != battery_warn) {
panels[i].battery.area.resize_needed = TRUE;
if (!battery_warn)
panels[i].battery.area.bg = panel_config.battery.area.bg;
schedule_panel_redraw();
}
}
@@ -421,6 +464,7 @@ gboolean resize_battery(void *obj)
void draw_battery(void *obj, cairo_t *c)
{
Battery *battery = (Battery *)obj;
Panel *panel = (Panel *)battery->area.panel;
draw_text_area(&battery->area,
c,
buf_bat_line1,
@@ -429,7 +473,14 @@ void draw_battery(void *obj, cairo_t *c)
bat2_font_desc,
battery->bat1_posy,
battery->bat2_posy,
&battery->font_color);
&battery->font_color,
panel->scale);
if (battery_warn && battery_warn_red) {
cairo_set_source_rgba(c, 1, 0, 0, 1);
cairo_set_line_width(c, 0);
cairo_rectangle(c, 0, 0, battery->area.width, battery->area.height);
cairo_fill(c);
}
}
void battery_dump_geometry(void *obj, int indent)

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);
@@ -79,7 +77,7 @@ void destroy_button(void *obj)
if (button->backend->instances) {
fprintf(stderr, "tint2: Error: Attempt to destroy backend while there are still frontend instances!\n");
exit(-1);
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) {
@@ -338,9 +338,9 @@ gboolean resize_button(void *obj)
Button *button = (Button *)obj;
Panel *panel = (Panel *)button->area.panel;
Area *area = &button->area;
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) {
@@ -349,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;
@@ -371,10 +371,9 @@ gboolean resize_button(void *obj)
available_h = panel->area.height;
}
int txt_height_ink, txt_height, txt_width;
int txt_height, txt_width;
if (button->backend->text) {
get_text_size2(button->backend->font_desc,
&txt_height_ink,
&txt_height,
&txt_width,
available_h,
@@ -383,9 +382,11 @@ gboolean resize_button(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;
}
gboolean result = FALSE;
@@ -438,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
@@ -459,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);
@@ -474,9 +478,10 @@ void draw_button(void *obj, cairo_t *c)
button->frontend->textx,
button->frontend->texty,
&button->backend->font_color,
panel_config.font_shadow);
panel_config.font_shadow ? layout : NULL);
g_object_unref(layout);
g_object_unref(context);
}
}

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;
@@ -110,8 +110,7 @@ void cleanup_clock()
clock_uwheel_command = NULL;
free(clock_dwheel_command);
clock_dwheel_command = NULL;
stop_timeout(clock_timeout);
clock_timeout = NULL;
destroy_timer(&clock_timer);
}
struct tm *clock_gettime_for_tz(const char *timezone)
@@ -130,17 +129,28 @@ struct tm *clock_gettime_for_tz(const char *timezone)
}
}
void update_clock_text(char *dst, size_t size, const char *format,
const char *timezone, bool *changed)
{
if (!dst || !format) {
return;
}
char tmp[256] = "";
strncpy(tmp, dst, sizeof(tmp) - 1);
strftime(dst, size, format, clock_gettime_for_tz(timezone));
*changed = *changed || strcmp(dst, tmp) != 0;
}
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) {
bool changed = false;
update_clock_text(buf_time, sizeof(buf_time), time1_format, time1_timezone, &changed);
update_clock_text(buf_date, sizeof(buf_date), time2_format, time2_timezone, &changed);
if (changed) {
for (int i = 0; i < num_panels; i++)
panels[i].clock.area.resize_needed = 1;
schedule_panel_redraw();
}
schedule_panel_redraw();
}
int ms_until_second_change(struct timeval* tm)
@@ -155,28 +165,7 @@ void update_clocks_sec(void *arg)
{
gettimeofday(&time_clock, 0);
update_clocks();
clock_timeout = add_timeout(ms_until_second_change(&time_clock), 0, update_clocks_sec, 0, &clock_timeout);
}
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;
clock_timeout = add_timeout(ms_until_second_change(&time_clock), 0, update_clocks_min, 0, &clock_timeout);
}
gboolean time_format_needs_sec_ticks(char *time_format)
{
if (!time_format)
return FALSE;
if (strchr(time_format, 'S') || strchr(time_format, 'T') || strchr(time_format, 'r'))
return TRUE;
return FALSE;
change_timer(&clock_timer, true, ms_until_second_change(&time_clock), 0, update_clocks_sec, 0);
}
void init_clock()
@@ -216,12 +205,8 @@ void init_clock_panel(void *p)
strftime(buf_tooltip, sizeof(buf_tooltip), time_tooltip_format, clock_gettime_for_tz(time_tooltip_timezone));
}
if (!clock_timeout) {
if (time_format_needs_sec_ticks(time1_format) || time_format_needs_sec_ticks(time2_format)) {
update_clocks_sec(NULL);
} else {
update_clocks_min(NULL);
}
if (!clock_timer.enabled_) {
update_clocks_sec(NULL);
}
}
@@ -262,10 +247,8 @@ void clock_default_font_changed()
}
void clock_compute_text_geometry(Clock *clock,
int *time_height_ink,
int *time_height,
int *time_width,
int *date_height_ink,
int *date_height,
int *date_width)
{
@@ -274,10 +257,8 @@ void clock_compute_text_geometry(Clock *clock,
time2_format ? buf_date : NULL,
time1_font_desc,
time2_font_desc,
time_height_ink,
time_height,
time_width,
date_height_ink,
date_height,
date_width);
}
@@ -307,6 +288,7 @@ gboolean resize_clock(void *obj)
void draw_clock(void *obj, cairo_t *c)
{
Clock *clock = (Clock *)obj;
Panel *panel = (Panel *)clock->area.panel;
draw_text_area(&clock->area,
c,
buf_time,
@@ -315,7 +297,8 @@ void draw_clock(void *obj, cairo_t *c)
time2_font_desc,
clock->time1_posy,
clock->time2_posy,
&clock->font);
&clock->font,
panel->scale);
}
void clock_dump_geometry(void *obj, int indent)

View File

@@ -244,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);
@@ -350,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 */
@@ -498,6 +508,8 @@ void add_entry(char *key, char *value)
wm_menu = atoi(value);
else if (strcmp(key, "panel_dock") == 0)
panel_dock = atoi(value);
else if (strcmp(key, "panel_pivot_struts") == 0)
panel_pivot_struts = atoi(value);
else if (strcmp(key, "urgent_nb_of_blink") == 0)
max_tick_urgent = atoi(value);
else if (strcmp(key, "panel_layer") == 0) {
@@ -782,7 +794,7 @@ void add_entry(char *key, char *value)
} else if (strcmp(key, "button_icon") == 0) {
if (strlen(value)) {
Button *button = get_or_create_last_button();
button->backend->icon_name = strdup(value);
button->backend->icon_name = expand_tilde(value);
}
} else if (strcmp(key, "button_text") == 0) {
if (strlen(value)) {
@@ -1006,6 +1018,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) {
@@ -1086,11 +1100,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) {
@@ -1133,8 +1154,11 @@ void add_entry(char *key, char *value)
} else if (strcmp(key, "systray_monitor") == 0) {
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);
}

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

View File

@@ -20,6 +20,8 @@
#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();
@@ -41,6 +43,7 @@ Execp *create_execp()
execp->backend->cache_icon = TRUE;
execp->backend->centered = TRUE;
execp->backend->font_color.alpha = 0.5;
INIT_TIMER(execp->backend->timer);
return execp;
}
@@ -67,14 +70,9 @@ 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_icon(execp->backend->icon);
free_and_null(execp->backend->buf_stdout);
free_and_null(execp->backend->buf_stderr);
free_and_null(execp->backend->text);
@@ -109,7 +107,7 @@ void destroy_execp(void *obj)
if (execp->backend->instances) {
fprintf(stderr, "tint2: Error: Attempt to destroy backend while there are still frontend instances!\n");
exit(-1);
exit(EXIT_FAILURE);
}
free(execp->backend);
free(execp);
@@ -196,7 +194,7 @@ void init_execp_panel(void *p)
execp->area.on_screen = TRUE;
instantiate_area_gradients(&execp->area);
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);
}
@@ -308,7 +306,6 @@ void execp_compute_icon_text_geometry(Execp *execp,
int *icon_w,
int *icon_h,
gboolean *text_next_line,
int *txt_height_ink,
int *txt_height,
int *txt_width,
int *new_size,
@@ -316,9 +313,9 @@ void execp_compute_icon_text_geometry(Execp *execp,
{
Panel *panel = (Panel *)execp->area.panel;
Area *area = &execp->area;
*horiz_padding = (panel_horizontal ? area->paddingxlr : area->paddingy);
*vert_padding = (panel_horizontal ? area->paddingy : area->paddingxlr);
*interior_padding = area->paddingx;
*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;
if (reload_icon(execp)) {
if (execp->backend->icon) {
@@ -337,7 +334,7 @@ void execp_compute_icon_text_geometry(Execp *execp,
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);
available_h = area->height - 2 * *vert_padding - left_right_border_width(area);
} else {
available_w = !text_next_line
? area->width - *icon_w - (*icon_w ? *interior_padding : 0) - 2 * *horiz_padding -
@@ -346,7 +343,6 @@ void execp_compute_icon_text_geometry(Execp *execp,
available_h = panel->area.height;
}
get_text_size2(execp->backend->font_desc,
txt_height_ink,
txt_height,
txt_width,
available_h,
@@ -355,7 +351,9 @@ void execp_compute_icon_text_geometry(Execp *execp,
strlen(execp->backend->text),
PANGO_WRAP_WORD_CHAR,
PANGO_ELLIPSIZE_NONE,
execp->backend->has_markup);
execp->backend->centered ? PANGO_ALIGN_CENTER : PANGO_ALIGN_LEFT,
execp->backend->has_markup,
panel->scale);
*resized = FALSE;
if (panel_horizontal) {
@@ -375,7 +373,11 @@ void execp_compute_icon_text_geometry(Execp *execp,
*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 (strlen(execp->backend->text)) {
*new_size = *icon_h + *interior_padding + *txt_height + 2 * *vert_padding + top_bottom_border_width(area);
} else {
*new_size = *icon_h + 2 * *vert_padding + top_bottom_border_width(area);
}
}
if (*new_size != area->height) {
*resized = TRUE;
@@ -389,7 +391,7 @@ int execp_compute_desired_size(void *obj)
int horiz_padding, vert_padding, interior_padding;
int icon_w, icon_h;
gboolean text_next_line;
int txt_height_ink, txt_height, txt_width;
int txt_height, txt_width;
int new_size;
gboolean resized;
execp_compute_icon_text_geometry(execp,
@@ -399,7 +401,6 @@ int execp_compute_desired_size(void *obj)
&icon_w,
&icon_h,
&text_next_line,
&txt_height_ink,
&txt_height,
&txt_width,
&new_size,
@@ -414,7 +415,7 @@ gboolean resize_execp(void *obj)
int horiz_padding, vert_padding, interior_padding;
int icon_w, icon_h;
gboolean text_next_line;
int txt_height_ink, txt_height, txt_width;
int txt_height, txt_width;
int new_size;
gboolean resized;
execp_compute_icon_text_geometry(execp,
@@ -424,7 +425,6 @@ gboolean resize_execp(void *obj)
&icon_w,
&icon_h,
&text_next_line,
&txt_height_ink,
&txt_height,
&txt_width,
&new_size,
@@ -445,10 +445,17 @@ gboolean resize_execp(void *obj)
execp->frontend->texty = (execp->area.height - txt_height) / 2;
execp->frontend->textx = execp->frontend->iconx + icon_w + interior_padding;
} else {
execp->frontend->icony = (execp->area.height - icon_h - interior_padding - txt_height) / 2;
execp->frontend->iconx = (execp->area.width - icon_w) / 2;
execp->frontend->texty = execp->frontend->icony + icon_h + interior_padding;
execp->frontend->textx = (execp->area.width - txt_width) / 2;
if (strlen(execp->backend->text)) {
execp->frontend->icony = (execp->area.height - icon_h - interior_padding - txt_height) / 2;
execp->frontend->iconx = (execp->area.width - icon_w) / 2;
execp->frontend->texty = execp->frontend->icony + icon_h + interior_padding;
execp->frontend->textx = (execp->area.width - txt_width) / 2;
} else {
execp->frontend->icony = (execp->area.height - icon_h) / 2;
execp->frontend->iconx = (execp->area.width - icon_w) / 2;
execp->frontend->texty = execp->frontend->icony + icon_h + interior_padding;
execp->frontend->textx = (execp->area.width - txt_width) / 2;
}
}
} else {
execp->frontend->texty = (execp->area.height - txt_height) / 2;
@@ -462,10 +469,17 @@ gboolean resize_execp(void *obj)
execp->frontend->texty = (execp->area.height - txt_height) / 2;
execp->frontend->textx = execp->frontend->iconx + icon_w + interior_padding;
} else {
execp->frontend->icony = (execp->area.height - icon_h - interior_padding - txt_height) / 2;
execp->frontend->iconx = left_border_width(&execp->area) + horiz_padding;
execp->frontend->texty = execp->frontend->icony + icon_h + interior_padding;
execp->frontend->textx = execp->frontend->iconx;
if (strlen(execp->backend->text)) {
execp->frontend->icony = (execp->area.height - icon_h - interior_padding - txt_height) / 2;
execp->frontend->iconx = left_border_width(&execp->area) + horiz_padding;
execp->frontend->texty = execp->frontend->icony + icon_h + interior_padding;
execp->frontend->textx = execp->frontend->iconx;
} else {
execp->frontend->icony = (execp->area.height - icon_h) / 2;
execp->frontend->iconx = left_border_width(&execp->area) + horiz_padding;
execp->frontend->texty = execp->frontend->icony + icon_h + interior_padding;
execp->frontend->textx = execp->frontend->iconx;
}
}
} else {
execp->frontend->texty = (execp->area.height - txt_height) / 2;
@@ -477,10 +491,27 @@ gboolean resize_execp(void *obj)
return resized;
}
PangoLayout *create_execp_text_layout(Execp *execp, PangoContext *context)
{
PangoLayout *layout = pango_layout_new(context);
pango_layout_set_font_description(layout, execp->backend->font_desc);
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);
return layout;
}
void draw_execp(void *obj, cairo_t *c)
{
Execp *execp = (Execp *)obj;
PangoLayout *layout = pango_cairo_create_layout(c);
Panel *panel = (Panel *)execp->area.panel;
PangoContext *context = pango_cairo_create_context(c);
pango_cairo_context_set_resolution(context, 96 * panel->scale);
PangoLayout *layout = create_execp_text_layout(execp, context);
PangoLayout *shadow_layout = NULL;
if (execp->backend->has_icon && execp->backend->icon) {
imlib_context_set_image(execp->backend->icon);
@@ -489,15 +520,18 @@ 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_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);
if (!execp->backend->has_markup)
if (!execp->backend->has_markup) {
pango_layout_set_text(layout, execp->backend->text, strlen(execp->backend->text));
else
} else {
pango_layout_set_markup(layout, execp->backend->text, strlen(execp->backend->text));
if (panel_config.font_shadow) {
shadow_layout = create_execp_text_layout(execp, context);
if (!layout_set_markup_strip_colors(shadow_layout, execp->backend->text)) {
g_object_unref(shadow_layout);
shadow_layout = NULL;
}
}
}
pango_cairo_update_layout(c, layout);
draw_text(layout,
@@ -505,9 +539,10 @@ void draw_execp(void *obj, cairo_t *c)
execp->frontend->textx,
execp->frontend->texty,
&execp->backend->font_color,
panel_config.font_shadow);
shadow_layout);
g_object_unref(layout);
g_object_unref(context);
}
void execp_dump_geometry(void *obj, int indent)
@@ -529,12 +564,13 @@ void execp_dump_geometry(void *obj, int indent)
imlib_context_set_image(tmp);
}
fprintf(stderr,
"tint2: %*sText: x = %d, y = %d, w = %d, align = %s, text = %s\n",
"tint2: %*sText: x = %d, y = %d, w = %d, h = %d, align = %s, text = %s\n",
indent,
"",
execp->frontend->textx,
execp->frontend->texty,
execp->frontend->textw,
execp->frontend->texth,
execp->backend->centered ? "center" : "left",
execp->backend->text);
}
@@ -544,10 +580,8 @@ void execp_force_update(Execp *execp)
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);
}
}
@@ -637,7 +671,8 @@ void execp_timer_callback(void *arg)
close(pipe_fd_stderr[0]);
return;
} else if (child == 0) {
fprintf(stderr, "tint2: 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_stdout[0]);
dup2(pipe_fd_stdout[1], 1); // 1 is stdout
@@ -761,8 +796,7 @@ gboolean read_execp(void *obj)
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";
@@ -857,7 +891,7 @@ gboolean read_execp(void *obj)
} else {
execp->backend->text = strdup("");
}
execp->backend->icon_path = strdup(execp->backend->buf_stdout);
execp->backend->icon_path = expand_tilde(execp->backend->buf_stdout);
}
size_t len = strlen(execp->backend->text);
if (len > 0 && execp->backend->text[len - 1] == '\n')
@@ -886,22 +920,22 @@ 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;
}
@@ -927,35 +961,39 @@ char *execp_get_tooltip(void *obj)
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);

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.
@@ -46,7 +48,7 @@ typedef struct ExecpBackend {
Background *bg;
// Backend state:
timeout *timer;
Timer timer;
int child_pipe_stdout;
int child_pipe_stderr;
pid_t child;

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;
}

View File

@@ -6,12 +6,19 @@
#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"
@@ -43,6 +50,15 @@ void handle_cli_arguments(int argc, char **argv)
} 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++;
@@ -75,7 +91,7 @@ void handle_cli_arguments(int argc, char **argv)
}
if (error) {
print_usage();
exit(1);
exit(EXIT_FAILURE);
}
}
}
@@ -88,6 +104,11 @@ void handle_env_vars()
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;
debug_blink = getenv("DEBUG_BLINK") != NULL;
thumb_use_shm = getenv("TINT2_THUMBNAIL_SHM") != NULL;
if (debug_fps) {
init_fps_distribution();
char *s = getenv("TRACING_FPS_THRESHOLD");
@@ -97,25 +118,25 @@ void handle_env_vars()
}
}
static timeout *detect_compositor_timer = NULL;
static Timer detect_compositor_timer = DEFAULT_TIMER;
static int detect_compositor_timer_counter = 0;
void detect_compositor(void *arg)
{
if (server.composite_manager) {
stop_timeout(detect_compositor_timer);
stop_timer(&detect_compositor_timer);
return;
}
detect_compositor_timer_counter--;
if (detect_compositor_timer_counter < 0) {
stop_timeout(detect_compositor_timer);
stop_timer(&detect_compositor_timer);
return;
}
// No compositor, check for one
if (XGetSelectionOwner(server.display, server.atom._NET_WM_CM_S0) != None) {
stop_timeout(detect_compositor_timer);
stop_timer(&detect_compositor_timer);
// Restart tint2
fprintf(stderr, "tint2: Detected compositor, restarting tint2...\n");
kill(getpid(), SIGUSR1);
@@ -128,15 +149,15 @@ void start_detect_compositor()
if (server.composite_manager)
return;
stop_timeout(detect_compositor_timer);
// Check every 0.5 seconds for up to 30 seconds
detect_compositor_timer_counter = 60;
detect_compositor_timer = add_timeout(500, 500, detect_compositor, 0, &detect_compositor_timer);
INIT_TIMER(detect_compositor_timer);
change_timer(&detect_compositor_timer, true, 500, 500, detect_compositor, 0);
}
void create_default_elements()
{
default_timeout();
default_timers();
default_systray();
memset(&server, 0, sizeof(server));
#ifdef ENABLE_BATTERY
@@ -151,6 +172,22 @@ void create_default_elements()
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();
@@ -161,20 +198,7 @@ void init_post_config()
imlib_context_set_colormap(server.colormap);
init_signals_postconfig();
// load default 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) {
fprintf(stderr,
RED "Could not load default_icon.png. Please check that tint2 has been installed correctly!" RESET
"\n");
}
load_default_task_icon();
XSync(server.display, False);
}
@@ -184,15 +208,16 @@ void init_X11_pre_config()
server.display = XOpenDisplay(NULL);
if (!server.display) {
fprintf(stderr, "tint2: could not open display!\n");
exit(1);
exit(EXIT_FAILURE);
}
server.x11_fd = ConnectionNumber(server.display);
XSetErrorHandler((XErrorHandler)server_catch_error);
XSetIOErrorHandler((XIOErrorHandler)x11_io_error);
XSetErrorHandler(server_catch_error);
XSetIOErrorHandler(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, "");
@@ -215,17 +240,18 @@ 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();
handle_env_vars();
init_X11_pre_config();
if (!config_read()) {
fprintf(stderr, "tint2: Could not read config file.\n");
print_usage();
warnings_for_timers = false;
cleanup();
return;
exit(EXIT_FAILURE);
}
init_post_config();
@@ -251,6 +277,8 @@ void cleanup()
#ifdef ENABLE_BATTERY
cleanup_battery();
#endif
cleanup_separator();
cleanup_taskbar();
cleanup_panel();
cleanup_config();
@@ -265,7 +293,7 @@ void cleanup()
xsettings_client = NULL;
cleanup_server();
cleanup_timeout();
cleanup_timers();
if (server.display)
XCloseDisplay(server.display);

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,23 +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') {
sprintf(q, "%c%c", '%', *p);
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

View File

@@ -48,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()
{
@@ -74,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));
@@ -535,41 +550,46 @@ gint compare_theme_directories(gconstpointer a, gconstpointer b, gpointer size_q
return abs(da->size - size) - abs(db->size - size);
}
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);
char *expanded = expand_tilde(s);
if (is_full_path(expanded) && file_exists(expanded))
return expanded;
free(expanded);
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
@@ -582,7 +602,7 @@ 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)) {
@@ -607,7 +627,7 @@ char *get_icon_path_helper(GSList *themes, const char *icon_name, int size)
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;
@@ -621,7 +641,7 @@ 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);
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)) {
@@ -662,12 +682,10 @@ 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;
}
@@ -676,18 +694,18 @@ char *get_icon_path_helper(GSList *themes, const char *icon_name, int size)
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);
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)) {
if (debug_icons)
fprintf(stderr, "tint2: Found %s\n", file_name);
g_slist_free(extensions);
return file_name;
} else {
free(file_name);
@@ -697,7 +715,6 @@ char *get_icon_path_helper(GSList *themes, const char *icon_name, int size)
}
}
g_slist_free(extensions);
return NULL;
}

View File

@@ -112,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();
@@ -174,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;
}
@@ -189,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;
@@ -204,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);
}
}
}
@@ -239,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);
@@ -281,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;
@@ -300,17 +303,17 @@ gboolean resize_launcher(void *obj)
// 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);
}
}
}
@@ -396,7 +399,12 @@ void launcher_icon_dump_geometry(void *obj, int indent)
Imlib_Image scale_icon(Imlib_Image original, int icon_size)
{
Imlib_Image icon_scaled;
if (original) {
if (!icon_size) {
icon_scaled = imlib_create_image(1, 1);
imlib_context_set_image(icon_scaled);
imlib_context_set_color(255, 255, 255, 255);
imlib_image_fill_rectangle(0, 0, icon_size, icon_size);
} else if (original) {
imlib_context_set_image(original);
icon_scaled = imlib_create_cropped_scaled_image(0,
0,
@@ -470,7 +478,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;

View File

@@ -262,7 +262,7 @@ void handle_event_property_notify(XEvent *e)
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_copy_text((Area *)task);
tooltip_update_contents_for((Area *)task);
tooltip_update();
}
if (taskbar_sort_method == TASKBAR_SORT_TITLE)
@@ -389,9 +389,9 @@ 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);
autohide_trigger_show(panel, e->xany.send_event);
else if (e->type == LeaveNotify)
autohide_trigger_hide(panel);
autohide_trigger_hide(panel, e->xany.send_event);
if (panel->is_hidden) {
if (e->type == ClientMessage && e->xclient.message_type == server.atom.XdndPosition) {
hidden_panel_shown_for_dnd = TRUE;
@@ -723,7 +723,7 @@ void handle_panel_refresh()
if (debug_frames) {
for (int i = 0; i < num_panels; i++) {
char path[256];
sprintf(path, "tint2-%d-panel-%d-frame-%d.png", getpid(), i, frame);
snprintf(path, sizeof(path), "tint2-%d-panel-%d-frame-%d.png", getpid(), i, frame);
save_panel_screenshot(&panels[i], path);
}
}
@@ -748,7 +748,7 @@ void run_tint2_event_loop()
// Wait for an event and handle it
ts_event_read = 0;
if (XPending(server.display) > 0 || select(max_fd + 1, &fds, 0, 0, get_next_timeout()) >= 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
@@ -797,6 +797,19 @@ void tint2(int argc, char **argv, gboolean *restart)
int main(int argc, char **argv)
{
#ifdef USE_REAL_MALLOC
if (!getenv("G_SLICE") && setenv("G_SLICE", "always-malloc", 1) == 0) {
fprintf(stderr,
YELLOW "tint2: reexecuting tint2 without glib slice allocator..." RESET "\n");
execvp(argv[0], argv);
fprintf(stderr, RED "tint2: %s %d: execvp failed! carrying on..." RESET "\n",
__FILE__, __LINE__);
}
#else
fprintf(stderr, "tint2: Using glib slice allocator (default). "
"Run tint2 with environment variable G_SLICE=always-malloc "
"in case of strange behavior or crashes\n");
#endif
gboolean restart;
do {
restart = FALSE;

View File

@@ -49,6 +49,7 @@ MouseAction mouse_tilt_right;
TaskbarMode taskbar_mode;
gboolean wm_menu;
gboolean panel_dock;
gboolean panel_pivot_struts;
Layer panel_layer;
PanelPosition panel_position;
gboolean panel_horizontal;
@@ -58,7 +59,8 @@ char *panel_window_name = NULL;
gboolean debug_geometry;
gboolean debug_gradients;
gboolean startup_notifications;
gboolean debug_thumbnails;
gboolean debug_blink;
gboolean panel_autohide;
int panel_autohide_show_timeout;
int panel_autohide_hide_timeout;
@@ -78,11 +80,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;
@@ -97,6 +104,7 @@ void default_panel()
panel_shrink = FALSE;
panel_strut_policy = STRUT_FOLLOW_SIZE;
panel_dock = FALSE; // default not in the dock
panel_pivot_struts = FALSE;
panel_layer = BOTTOM_LAYER; // default is bottom layer
panel_window_name = strdup("tint2");
wm_menu = FALSE;
@@ -129,8 +137,6 @@ void cleanup_panel()
if (!panels)
return;
cleanup_taskbar();
for (int i = 0; i < num_panels; i++) {
Panel *p = &panels[i];
@@ -144,7 +150,7 @@ 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);
}
@@ -206,6 +212,7 @@ 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,
@@ -218,6 +225,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;
@@ -298,7 +316,7 @@ void init_panel()
}
if (panel_autohide)
autohide_trigger_hide(p);
autohide_trigger_hide(p, false);
}
taskbar_refresh_tasklist();
@@ -355,6 +373,19 @@ void panel_compute_size(Panel *panel)
}
}
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)
@@ -449,9 +480,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) {
@@ -468,9 +499,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;
}
@@ -493,9 +524,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)
@@ -512,6 +545,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++) {
@@ -536,6 +612,21 @@ gboolean resize_panel(void *obj)
return FALSE;
}
#define STRUT_LEFT 0
#define STRUT_RIGHT 1
#define STRUT_TOP 2
#define STRUT_BOTTOM 3
#define STRUT_LEFT_Y1 4
#define STRUT_LEFT_Y2 5
#define STRUT_RIGHT_Y1 6
#define STRUT_RIGHT_Y2 7
#define STRUT_TOP_X1 8
#define STRUT_TOP_X2 9
#define STRUT_BOTTOM_X1 10
#define STRUT_BOTTOM_X2 11
#define STRUT_COUNT 12
#define STRUT_COUNT_OLD 4
void update_strut(Panel *p)
{
if (panel_strut_policy == STRUT_NONE) {
@@ -550,36 +641,36 @@ void update_strut(Panel *p)
int d3;
XGetGeometry(server.display, server.root_win, &d2, &d3, &d3, &screen_width, &screen_height, &d1, &d1);
Monitor monitor = server.monitors[p->monitor];
long struts[12] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
if (panel_horizontal) {
long struts[STRUT_COUNT] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
if (panel_horizontal ^ panel_pivot_struts) {
int height = p->area.height + p->marginy;
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;
struts[8] = p->posx;
struts[STRUT_TOP] = height + monitor.y;
struts[STRUT_TOP_X1] = p->posx;
// p->area.width - 1 allowed full screen on monitor 2
struts[9] = p->posx + p->area.width - 1;
struts[STRUT_TOP_X2] = p->posx + p->area.width - 1;
} else {
struts[3] = height + screen_height - monitor.y - monitor.height;
struts[10] = p->posx;
struts[STRUT_BOTTOM] = height + screen_height - monitor.y - monitor.height;
struts[STRUT_BOTTOM_X1] = p->posx;
// p->area.width - 1 allowed full screen on monitor 2
struts[11] = p->posx + p->area.width - 1;
struts[STRUT_BOTTOM_X2] = p->posx + p->area.width - 1;
}
} else {
int width = p->area.width + p->marginx;
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;
struts[4] = p->posy;
struts[STRUT_LEFT] = width + monitor.x;
struts[STRUT_LEFT_Y1] = p->posy;
// p->area.width - 1 allowed full screen on monitor 2
struts[5] = p->posy + p->area.height - 1;
struts[STRUT_LEFT_Y2] = p->posy + p->area.height - 1;
} else {
struts[1] = width + screen_width - monitor.x - monitor.width;
struts[6] = p->posy;
struts[STRUT_RIGHT] = width + screen_width - monitor.x - monitor.width;
struts[STRUT_RIGHT_Y1] = p->posy;
// p->area.width - 1 allowed full screen on monitor 2
struts[7] = p->posy + p->area.height - 1;
struts[STRUT_RIGHT_Y2] = p->posy + p->area.height - 1;
}
}
// Old specification : fluxbox need _NET_WM_STRUT.
@@ -590,7 +681,7 @@ void update_strut(Panel *p)
32,
PropModeReplace,
(unsigned char *)&struts,
4);
STRUT_COUNT_OLD);
XChangeProperty(server.display,
p->main_win,
server.atom._NET_WM_STRUT_PARTIAL,
@@ -598,7 +689,7 @@ void update_strut(Panel *p)
32,
PropModeReplace,
(unsigned char *)&struts,
12);
STRUT_COUNT);
}
void set_panel_items_order(Panel *p)
@@ -1020,15 +1111,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);
@@ -1040,7 +1131,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
@@ -1048,26 +1139,27 @@ void autohide_hide(void *p)
schedule_panel_redraw();
}
void autohide_trigger_show(Panel *p)
void autohide_trigger_show(Panel *p, bool forced)
{
if (!p)
return;
change_timeout(&p->autohide_timeout, panel_autohide_show_timeout, 0, autohide_show, p);
change_timer(&p->autohide_timer, true, forced ? 0 : panel_autohide_show_timeout, 0, autohide_show, p);
}
void autohide_trigger_hide(Panel *p)
void autohide_trigger_hide(Panel *p, bool forced)
{
if (!p)
return;
Window root, child;
int xr, yr, xw, yw;
unsigned int mask;
if (XQueryPointer(server.display, p->main_win, &root, &child, &xr, &yr, &xw, &yw, &mask))
if (child)
return; // mouse over one of the system tray icons
change_timeout(&p->autohide_timeout, panel_autohide_hide_timeout, 0, autohide_hide, p);
if (!forced) {
Window root, child;
int xr, yr, xw, yw;
unsigned int mask;
if (XQueryPointer(server.display, p->main_win, &root, &child, &xr, &yr, &xw, &yw, &mask))
if (child)
return; // mouse over one of the system tray icons
}
change_timer(&p->autohide_timer, true, forced ? 0 : panel_autohide_hide_timeout, 0, autohide_hide, p);
}
void shrink_panel(Panel *panel)

View File

@@ -69,6 +69,7 @@ typedef enum Strut {
extern TaskbarMode taskbar_mode;
extern gboolean wm_menu;
extern gboolean panel_dock;
extern gboolean panel_pivot_struts;
extern Layer panel_layer;
extern char *panel_window_name;
extern PanelPosition panel_position;
@@ -94,6 +95,11 @@ 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;
extern gboolean thumb_use_shm;
extern gboolean debug_blink;
typedef struct Panel {
Area area;
@@ -116,6 +122,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 +150,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;
@@ -196,8 +203,8 @@ Button *click_button(Panel *panel, int x, int y);
void autohide_show(void *p);
void autohide_hide(void *p);
void autohide_trigger_show(Panel *p);
void autohide_trigger_hide(Panel *p);
void autohide_trigger_show(Panel *p, bool forced);
void autohide_trigger_hide(Panel *p, bool forced);
const char *get_default_font();

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

@@ -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);
}
}
@@ -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;
@@ -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);
}
}
@@ -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) {
@@ -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);
@@ -940,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);
@@ -1055,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) {
@@ -1071,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,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) {
@@ -1150,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
@@ -1224,8 +1223,7 @@ 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",
@@ -1244,8 +1242,7 @@ 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",
@@ -1257,10 +1254,7 @@ void systray_render_icon_composited(void *t)
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
@@ -1423,9 +1417,7 @@ void systray_render_icon(void *t)
// __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;
}
@@ -1448,19 +1440,13 @@ 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->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);
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->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);
systray_render_icon_from_image(traywin);
if (systray_profile)
fprintf(stderr,

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

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, "tint2: %s %d: win = %ld, task = %s\n", __func__, __LINE__, win, task_template.title ?
// task_template.title : "??");
// fprintf(stderr, "tint2: 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, "tint2: %s %d: win = %ld hiding task: another desktop\n", __func__, __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];
@@ -186,9 +217,6 @@ void remove_task(Task *task)
if (!task)
return;
// fprintf(stderr, "tint2: %s %d: win = %ld, task = %s\n", __func__, __LINE__, task->win, task->title ? task->title :
// "??");
if (taskbar_mode == MULTI_DESKTOP) {
Panel *panel = task->area.panel;
panel->area.resize_needed = 1;
@@ -196,11 +224,14 @@ void remove_task(Task *task)
Window win = task->win;
// free title and icon just for the first task
// free title, icon and application name just for the first task
// even with task_on_all_desktop and with task_on_all_panel
// fprintf(stderr, "tint2: remove_task %s %d\n", task->title, task->desktop);
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);
@@ -212,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);
}
@@ -266,36 +299,29 @@ 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;
task_remove_icon(task);
Imlib_Image img = NULL;
if (!img) {
int len;
gulong *data = server_get_property(task->win, server.atom._NET_WM_ICON, XA_CARDINAL, &len);
gulong *data = server_get_property(win, server.atom._NET_WM_ICON, XA_CARDINAL, &len);
if (data) {
if (len > 0) {
// get ARGB icon
int w, h;
gulong *tmp_data = get_best_icon(data, get_icon_count(data, len), len, &w, &h, panel->g_task.icon_size1);
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);
if (0 && img)
fprintf(stderr,
"%s: Got %dx%d icon via _NET_WM_ICON for %s\n",
__func__,
w,
h,
task->title ? task->title : "task");
int array_size = w * h;
// imlib needs the array in DATA32 type
// using malloc for the array to protect from stack overflow
DATA32 *icon_data = (DATA32*) g_try_malloc(sizeof(*icon_data) * array_size);
if (icon_data) {
for (int j = 0; j < array_size; ++j)
icon_data[j] = tmp_data[j];
img = imlib_create_image_using_copied_data(w, h, icon_data);
g_free(icon_data);
}
}
}
XFree(data);
@@ -303,7 +329,7 @@ void task_update_icon(Task *task)
}
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
@@ -315,13 +341,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",
__func__,
w,
h,
task->title ? task->title : "task");
}
XFree(hints);
}
@@ -332,6 +351,44 @@ void task_update_icon(Task *task)
img = imlib_clone_image();
}
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);
@@ -345,20 +402,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,
@@ -376,9 +423,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];
@@ -403,7 +453,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
@@ -433,11 +483,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);
@@ -451,9 +503,10 @@ void draw_task(void *obj, cairo_t *c)
task->_text_posy = (panel->g_task.area.height - task->_text_height) / 2.0;
Color *config_text = &panel->g_task.font[task->current_state];
draw_text(layout, c, panel->g_task.text_posx, task->_text_posy, config_text, panel->font_shadow);
draw_text(layout, c, panel->g_task.text_posx, task->_text_posy, config_text, panel->font_shadow ? layout : NULL);
g_object_unref(layout);
g_object_unref(context);
}
if (panel->g_task.has_icon)
@@ -484,11 +537,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;
}
@@ -591,7 +662,6 @@ void reset_active_task()
}
Window w1 = get_active_window();
// fprintf(stderr, "tint2: Change active task %ld\n", w1);
if (w1) {
if (!get_task_buttons(w1)) {
@@ -603,11 +673,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) {
@@ -673,8 +782,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);
@@ -701,8 +810,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)
@@ -712,10 +821,8 @@ 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)
@@ -783,7 +890,6 @@ void task_handle_mouse_event(Task *task, MouseAction action)
void task_update_desktop(Task *task)
{
// fprintf(stderr, "tint2: %s %d:\n", __func__, __LINE__);
Window win = task->win;
remove_task(task);
task = add_task(win);

View File

@@ -42,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.
@@ -61,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;
@@ -74,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);
@@ -91,6 +100,7 @@ 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;
@@ -46,8 +47,12 @@ 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);
@@ -55,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);
@@ -73,7 +80,6 @@ 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;
@@ -82,6 +88,7 @@ void default_taskbar()
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();
@@ -107,7 +114,10 @@ void taskbar_save_orderings()
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) {
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;
@@ -120,6 +130,10 @@ void taskbar_save_orderings()
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)) {
@@ -152,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]);
@@ -166,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);
@@ -216,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;
}
@@ -282,27 +304,28 @@ void init_taskbar_panel(void *p)
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) {
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_");
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;
@@ -310,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,
@@ -321,17 +343,19 @@ 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_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) - top_bottom_border_width(&panel->g_task.area);
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;
}
@@ -353,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()
@@ -422,8 +462,8 @@ int compare_windows(const void *a, const void *b)
if (!sort_windows)
return 0;
int ia = *(int*)a;
int ib = *(int*)b;
int ia = *(int *)a;
int ib = *(int *)b;
Window wina = sort_windows[ia];
Window winb = sort_windows[ib];
@@ -433,7 +473,7 @@ int compare_windows(const void *a, const void *b)
int posb = -1;
int pos = 0;
for (GList *item = (GList *)order->data; item; item = item->next, pos++) {
Window win = *(Window*)item->data;
Window win = *(Window *)item->data;
if (win == wina)
posa = pos;
if (win == winb)
@@ -467,7 +507,6 @@ void taskbar_refresh_tasklist()
{
if (!taskbar_enabled)
return;
// fprintf(stderr, "tint2: %s %d:\n", __func__, __LINE__);
int num_results;
Window *win = server_get_property(server.root_win, server.atom._NET_CLIENT_LIST, XA_WINDOW, &num_results);
@@ -521,7 +560,6 @@ gboolean resize_taskbar(void *obj)
Taskbar *taskbar = (Taskbar *)obj;
Panel *panel = (Panel *)taskbar->area.panel;
// fprintf(stderr, "tint2: resize_taskbar %d %d\n", taskbar->area.posx, taskbar->area.posy);
if (panel_horizontal) {
relayout_with_constraint(&taskbar->area, panel->g_task.maximum_width);
@@ -536,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;
}
@@ -697,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);
@@ -708,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) {
@@ -779,3 +827,49 @@ 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;
@@ -72,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);
@@ -210,9 +215,10 @@ void draw_taskbarname(void *obj, cairo_t *c)
cairo_set_source_rgba(c, config_text->rgb[0], config_text->rgb[1], config_text->rgb[2], config_text->alpha);
pango_cairo_update_layout(c, layout);
draw_text(layout, c, 0, taskbar_name->posy, config_text, ((Panel *)taskbar_name->area.panel)->font_shadow);
draw_text(layout, c, 0, taskbar_name->posy, config_text, ((Panel *)taskbar_name->area.panel)->font_shadow ? layout : NULL);
g_object_unref(layout);
g_object_unref(context);
}
void update_desktop_names()

12
src/tint2-send/Makefile Normal file
View File

@@ -0,0 +1,12 @@
ifeq ($(PREFIX),)
PREFIX := /usr/local
endif
tint2-send: tint2-send.c
$(CC) tint2-send.c -lX11 -o tint2-send
install: tint2-send
install -m 755 tint2-send $(DESTDIR)/$(PREFIX)/bin/
clean:
rm -f tint2-send

128
src/tint2-send/tint2-send.c Normal file
View File

@@ -0,0 +1,128 @@
#include <X11/Xlib.h>
#include <X11/Xutil.h>
#include <X11/Xatom.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
static Display *display = 0;
/* From wmctrl */
char *get_property(Window window, Atom xa_prop_type, const char *prop_name) {
Atom xa_prop_name = XInternAtom(display, prop_name, False);
Atom xa_ret_type;
int ret_format;
unsigned long ret_nitems;
unsigned long ret_bytes_after;
unsigned long tmp_size;
unsigned char *ret_prop;
if (XGetWindowProperty(display, window, xa_prop_name, 0, 1024,
False, xa_prop_type, &xa_ret_type, &ret_format,
&ret_nitems, &ret_bytes_after, &ret_prop) != Success) {
return NULL;
}
if (xa_ret_type != xa_prop_type) {
XFree(ret_prop);
return NULL;
}
/* Correct 64 Architecture implementation of 32 bit data */
tmp_size = (ret_format / 8) * ret_nitems;
if (ret_format == 32)
tmp_size *= sizeof(long) / 4;
char *ret = (char *)calloc(1, tmp_size + 1);
memcpy(ret, ret_prop, tmp_size);
XFree(ret_prop);
return ret;
}
int is_tint2(Window window)
{
XWindowAttributes attr = {};
if (!XGetWindowAttributes(display, window, &attr))
return 0;
if (attr.map_state != IsViewable)
return 0;
char *wm_class = get_property(window, XA_STRING, "WM_NAME");
if (!wm_class) {
return 0;
}
int class_match = 0;
if (strcmp(wm_class, "tint2") == 0) {
class_match = 1;
}
free(wm_class);
return class_match;
}
void handle_tint2_window(Window window, void *arg)
{
if (!is_tint2(window))
return;
char *action = (char *)arg;
if (strcmp(action, "show") == 0) {
fprintf(stderr, "Showing tint2 window: %lx\n", window);
XEvent event = {};
event.xcrossing.type = EnterNotify;
event.xcrossing.window = window;
event.xcrossing.mode = NotifyNormal;
event.xcrossing.detail = NotifyVirtual;
event.xcrossing.same_screen = True;
XSendEvent(display, window, False, 0, &event);
XFlush(display);
} else if (strcmp(action, "hide") == 0) {
fprintf(stderr, "Hiding tint2 window: %lx\n", window);
XEvent event = {};
event.xcrossing.type = LeaveNotify;
event.xcrossing.window = window;
event.xcrossing.mode = NotifyNormal;
event.xcrossing.detail = NotifyVirtual;
event.xcrossing.same_screen = True;
XSendEvent(display, window, False, 0, &event);
XFlush(display);
} else {
fprintf(stderr, "Error: unknown action %s\n", action);
}
}
typedef void window_callback_t(Window window, void *arg);
void walk_windows(Window node, window_callback_t *callback, void *arg)
{
callback(node, arg);
Window root = 0;
Window parent = 0;
Window *children = 0;
unsigned int nchildren = 0;
if (!XQueryTree(display, node,
&root, &parent, &children, &nchildren)) {
return;
}
for (unsigned int i = 0; i < nchildren; i++) {
walk_windows(children[i], callback, arg);
}
}
int main(int argc, char **argv)
{
display = XOpenDisplay(NULL);
if (!display ) {
fprintf(stderr, "Failed to open X11 connection\n");
exit(1);
}
argc--, argv++;
if (!argc) {
fprintf(stderr, "Usage: tint2-show [show|hide]\n");
exit(1);
}
char *action = argv[0];
walk_windows(DefaultRootWindow(display), handle_tint2_window, action);
return 0;
}

View File

@@ -7,7 +7,7 @@ pkg_check_modules( GLIB2 REQUIRED glib-2.0 )
pkg_check_modules( GOBJECT2 REQUIRED gobject-2.0 )
pkg_check_modules( IMLIB2 REQUIRED imlib2 )
pkg_check_modules( GTHREAD2 REQUIRED gthread-2.0 )
pkg_check_modules( GTK2 REQUIRED gtk+-x11-2.0 )
pkg_check_modules( GTK3 REQUIRED gtk+-x11-3.0 )
pkg_check_modules( RSVG librsvg-2.0>=2.36.0 )
include_directories( ../util
@@ -16,15 +16,20 @@ include_directories( ../util
${GOBJECT2_INCLUDE_DIRS}
${IMLIB2_INCLUDE_DIRS}
${GTHREAD2_INCLUDE_DIRS}
${GTK2_INCLUDE_DIRS}
${GTK3_INCLUDE_DIRS}
${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
@@ -50,7 +55,7 @@ link_directories( ${X11_T2C_LIBRARY_DIRS}
${GOBJECT2_LIBRARY_DIRS}
${IMLIB2_LIBRARY_DIRS}
${GTHREAD2_LIBRARY_DIRS}
${GTK2_LIBRARY_DIRS}
${GTK3_LIBRARY_DIRS}
${RSVG_LIBRARY_DIRS} )
add_executable( tint2conf ${SOURCES} )
target_link_libraries( tint2conf ${X11_T2C_LIBRARIES}
@@ -58,13 +63,14 @@ target_link_libraries( tint2conf ${X11_T2C_LIBRARIES}
${GOBJECT2_LIBRARIES}
${IMLIB2_LIBRARIES}
${GTHREAD2_LIBRARIES}
${GTK2_LIBRARIES}
${GTK3_LIBRARIES}
${RSVG_LIBRARIES} )
add_definitions( -DINSTALL_PREFIX=\"${CMAKE_INSTALL_PREFIX}\" )
add_definitions( -DLOCALEDIR=\"${CMAKE_INSTALL_FULL_LOCALEDIR}\" )
add_definitions( -DGETTEXT_PACKAGE=\"tint2conf\" )
set_target_properties( tint2conf PROPERTIES COMPILE_FLAGS "-Wall -pthread -std=c99" )
add_definitions( -DDGLIB_DISABLE_DEPRECATION_WARNINGS=1 )
set_target_properties( tint2conf PROPERTIES COMPILE_FLAGS "-Wall -Wextra -Wno-unused-parameter -Wno-sign-compare -Wpointer-arith -fno-strict-aliasing -pthread -std=c99 -Werror-implicit-function-declaration -Wno-deprecated -Wno-deprecated-declarations" )
set_target_properties( tint2conf PROPERTIES LINK_FLAGS "-pthread" )
add_subdirectory(po)

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)
{
@@ -95,7 +95,7 @@ void create_background(GtkWidget *parent)
backgrounds = gtk_list_store_new(bgNumCols,
GDK_TYPE_PIXBUF,
GDK_TYPE_COLOR,
GTK_TYPE_INT,
GTK_TYPE_SHADOW_TYPE,
GDK_TYPE_COLOR,
GTK_TYPE_INT,
GTK_TYPE_INT,
@@ -115,11 +115,12 @@ 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;
GtkTooltips *tooltips = gtk_tooltips_new();
table = gtk_table_new(1, 4, FALSE);
gtk_widget_show(table);
@@ -139,21 +140,21 @@ void create_background(GtkWidget *parent)
gtk_widget_show(current_background);
gtk_table_attach(GTK_TABLE(table), current_background, col, col + 1, row, row + 1, GTK_FILL, 0, 0, 0);
col++;
gtk_tooltips_set_tip(tooltips, current_background, _("Selects the background you would like to modify"), NULL);
gtk_widget_set_tooltip_text(current_background, _("Selects the background you would like to modify"));
button = gtk_button_new_from_stock("gtk-add");
gtk_signal_connect(GTK_OBJECT(button), "clicked", GTK_SIGNAL_FUNC(background_duplicate), NULL);
g_signal_connect(button, "clicked", G_CALLBACK(background_duplicate), NULL);
gtk_widget_show(button);
gtk_table_attach(GTK_TABLE(table), button, col, col + 1, row, row + 1, GTK_FILL, 0, 0, 0);
col++;
gtk_tooltips_set_tip(tooltips, button, _("Creates a copy of the current background"), NULL);
gtk_widget_set_tooltip_text(button, _("Creates a copy of the current background"));
button = gtk_button_new_from_stock("gtk-remove");
gtk_signal_connect(GTK_OBJECT(button), "clicked", GTK_SIGNAL_FUNC(background_delete), NULL);
g_signal_connect(button, "clicked", G_CALLBACK(background_delete), NULL);
gtk_widget_show(button);
gtk_table_attach(GTK_TABLE(table), button, col, col + 1, row, row + 1, GTK_FILL, 0, 0, 0);
col++;
gtk_tooltips_set_tip(tooltips, button, _("Deletes the current background"), NULL);
gtk_widget_set_tooltip_text(button, _("Deletes the current background"));
table = gtk_table_new(4, 4, FALSE);
gtk_widget_show(table);
@@ -173,7 +174,20 @@ void create_background(GtkWidget *parent)
gtk_widget_show(background_fill_color);
gtk_table_attach(GTK_TABLE(table), background_fill_color, col, col + 1, row, row + 1, GTK_FILL, 0, 0, 0);
col++;
gtk_tooltips_set_tip(tooltips, background_fill_color, _("The fill color of the current background"), NULL);
gtk_widget_set_tooltip_text(background_fill_color, _("The fill color of the current background"));
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_widget_set_tooltip_text(background_fill_content_tint_weight, _("How much the border color should be tinted with the content color"));
row++, col = 2;
label = gtk_label_new(_("Border color"));
@@ -187,7 +201,20 @@ void create_background(GtkWidget *parent)
gtk_widget_show(background_border_color);
gtk_table_attach(GTK_TABLE(table), background_border_color, col, col + 1, row, row + 1, GTK_FILL, 0, 0, 0);
col++;
gtk_tooltips_set_tip(tooltips, background_border_color, _("The border color of the current background"), NULL);
gtk_widget_set_tooltip_text(background_border_color, _("The border color of the current background"));
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_widget_set_tooltip_text(background_border_content_tint_weight, _("How much the border color should be tinted with the content color"));
row++, col = 2;
label = gtk_label_new(_("Gradient"));
@@ -213,10 +240,8 @@ void create_background(GtkWidget *parent)
gtk_widget_show(background_fill_color_over);
gtk_table_attach(GTK_TABLE(table), background_fill_color_over, col, col + 1, row, row + 1, GTK_FILL, 0, 0, 0);
col++;
gtk_tooltips_set_tip(tooltips,
background_fill_color_over,
_("The fill color of the current background on mouse over"),
NULL);
gtk_widget_set_tooltip_text(background_fill_color_over,
_("The fill color of the current background on mouse over"));
row++, col = 2;
label = gtk_label_new(_("Border color (mouse over)"));
@@ -230,10 +255,9 @@ void create_background(GtkWidget *parent)
gtk_widget_show(background_border_color_over);
gtk_table_attach(GTK_TABLE(table), background_border_color_over, col, col + 1, row, row + 1, GTK_FILL, 0, 0, 0);
col++;
gtk_tooltips_set_tip(tooltips,
gtk_widget_set_tooltip_text(
background_border_color_over,
_("The border color of the current background on mouse over"),
NULL);
_("The border color of the current background on mouse over"));
row++, col = 2;
label = gtk_label_new(_("Gradient (mouse over)"));
@@ -259,10 +283,8 @@ void create_background(GtkWidget *parent)
gtk_widget_show(background_fill_color_press);
gtk_table_attach(GTK_TABLE(table), background_fill_color_press, col, col + 1, row, row + 1, GTK_FILL, 0, 0, 0);
col++;
gtk_tooltips_set_tip(tooltips,
background_fill_color_press,
_("The fill color of the current background on mouse button press"),
NULL);
gtk_widget_set_tooltip_text(background_fill_color_press,
_("The fill color of the current background on mouse button press"));
row++, col = 2;
label = gtk_label_new(_("Border color (pressed)"));
@@ -276,10 +298,8 @@ void create_background(GtkWidget *parent)
gtk_widget_show(background_border_color_press);
gtk_table_attach(GTK_TABLE(table), background_border_color_press, col, col + 1, row, row + 1, GTK_FILL, 0, 0, 0);
col++;
gtk_tooltips_set_tip(tooltips,
background_border_color_press,
_("The border color of the current background on mouse button press"),
NULL);
gtk_widget_set_tooltip_text(background_border_color_press,
_("The border color of the current background on mouse button press"));
row++, col = 2;
label = gtk_label_new(_("Gradient (pressed)"));
@@ -304,10 +324,8 @@ void create_background(GtkWidget *parent)
gtk_widget_show(background_border_width);
gtk_table_attach(GTK_TABLE(table), background_border_width, col, col + 1, row, row + 1, GTK_FILL, 0, 0, 0);
col++;
gtk_tooltips_set_tip(tooltips,
background_border_width,
_("The width of the border of the current background, in pixels"),
NULL);
gtk_widget_set_tooltip_text(background_border_width,
_("The width of the border of the current background, in pixels"));
row++, col = 2;
label = gtk_label_new(_("Corner radius"));
@@ -320,7 +338,7 @@ void create_background(GtkWidget *parent)
gtk_widget_show(background_corner_radius);
gtk_table_attach(GTK_TABLE(table), background_corner_radius, col, col + 1, row, row + 1, GTK_FILL, 0, 0, 0);
col++;
gtk_tooltips_set_tip(tooltips, background_corner_radius, _("The corner radius of the current background"), NULL);
gtk_widget_set_tooltip_text(background_corner_radius, _("The corner radius of the current background"));
row++;
col = 2;
@@ -366,6 +384,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);
}
@@ -682,9 +702,8 @@ void background_update_image(int index)
g_boxed_free(GDK_TYPE_COLOR, fillColor);
g_boxed_free(GDK_TYPE_COLOR, borderColor);
GdkPixmap *pixmap = gdk_pixmap_new(NULL, w, h, 24);
cairo_t *cr = gdk_cairo_create(pixmap);
cairo_surface_t *s = cairo_image_surface_create(CAIRO_FORMAT_RGB24, w, h);
cairo_t *cr = cairo_create(s);
cairo_set_line_width(cr, b);
cairo_set_source_rgb(cr, 0.5, 0.5, 0.5);
@@ -714,13 +733,12 @@ void background_update_image(int index)
cairo_destroy(cr);
cr = NULL;
pixbuf = gdk_pixbuf_get_from_drawable(NULL, pixmap, gdk_colormap_get_system(), 0, 0, 0, 0, w, h);
if (pixmap)
g_object_unref(pixmap);
pixbuf = gdk_pixbuf_get_from_surface(s, 0, 0, w, h);
gtk_list_store_set(backgrounds, &iter, bgColPixbuf, pixbuf, -1);
if (pixbuf)
g_object_unref(pixbuf);
cairo_surface_destroy(s);
}
void background_force_update()
@@ -750,6 +768,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 +857,10 @@ void background_update(GtkWidget *widget, gpointer data)
sideLeft,
bgColBorderSidesRight,
sideRight,
bgColFillWeight,
fill_weight,
bgColBorderWeight,
border_weight,
-1);
background_update_image(index);
}
@@ -862,6 +887,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 +902,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 +968,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 +1002,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

@@ -32,7 +32,7 @@ GtkWidget *create_gradient_combo()
void create_gradient(GtkWidget *parent)
{
gradient_ids = gtk_list_store_new(grNumCols, GDK_TYPE_PIXBUF, GTK_TYPE_INT, GTK_TYPE_STRING);
gradient_ids = gtk_list_store_new(grNumCols, GDK_TYPE_PIXBUF, G_TYPE_INT, G_TYPE_STRING);
gradients = NULL;
gradient_stop_ids = gtk_list_store_new(grStopNumCols, GDK_TYPE_PIXBUF);
@@ -61,13 +61,13 @@ void create_gradient(GtkWidget *parent)
col++;
button = gtk_button_new_from_stock("gtk-add");
gtk_signal_connect(GTK_OBJECT(button), "clicked", GTK_SIGNAL_FUNC(gradient_duplicate), NULL);
g_signal_connect(GTK_OBJECT(button), "clicked", GTK_SIGNAL_FUNC(gradient_duplicate), NULL);
gtk_widget_show(button);
gtk_table_attach(GTK_TABLE(table), button, col, col + 1, row, row + 1, GTK_FILL, 0, 0, 0);
col++;
button = gtk_button_new_from_stock("gtk-remove");
gtk_signal_connect(GTK_OBJECT(button), "clicked", GTK_SIGNAL_FUNC(gradient_delete), NULL);
g_signal_connect(GTK_OBJECT(button), "clicked", GTK_SIGNAL_FUNC(gradient_delete), NULL);
gtk_widget_show(button);
gtk_table_attach(GTK_TABLE(table), button, col, col + 1, row, row + 1, GTK_FILL, 0, 0, 0);
col++;
@@ -85,13 +85,13 @@ void create_gradient(GtkWidget *parent)
gtk_table_attach(GTK_TABLE(table), label, col, col + 1, row, row + 1, GTK_FILL, 0, 0, 0);
col++;
gradient_combo_type = gtk_combo_box_new_text();
gradient_combo_type = gtk_combo_box_text_new();
gtk_widget_show(gradient_combo_type);
gtk_table_attach(GTK_TABLE(table), gradient_combo_type, col, col + 1, row, row + 1, GTK_FILL, 0, 0, 0);
col++;
gtk_combo_box_append_text(GTK_COMBO_BOX(gradient_combo_type), _("Vertical"));
gtk_combo_box_append_text(GTK_COMBO_BOX(gradient_combo_type), _("Horizontal"));
gtk_combo_box_append_text(GTK_COMBO_BOX(gradient_combo_type), _("Radial"));
gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(gradient_combo_type), _("Vertical"));
gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(gradient_combo_type), _("Horizontal"));
gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(gradient_combo_type), _("Radial"));
gtk_combo_box_set_active(GTK_COMBO_BOX(gradient_combo_type), 0);
row++, col = 2;
@@ -142,13 +142,13 @@ void create_gradient(GtkWidget *parent)
col++;
button = gtk_button_new_from_stock("gtk-add");
gtk_signal_connect(GTK_OBJECT(button), "clicked", GTK_SIGNAL_FUNC(gradient_stop_duplicate), NULL);
g_signal_connect(GTK_OBJECT(button), "clicked", GTK_SIGNAL_FUNC(gradient_stop_duplicate), NULL);
gtk_widget_show(button);
gtk_table_attach(GTK_TABLE(table), button, col, col + 1, row, row + 1, GTK_FILL, 0, 0, 0);
col++;
button = gtk_button_new_from_stock("gtk-remove");
gtk_signal_connect(GTK_OBJECT(button), "clicked", GTK_SIGNAL_FUNC(gradient_stop_delete), NULL);
g_signal_connect(GTK_OBJECT(button), "clicked", GTK_SIGNAL_FUNC(gradient_stop_delete), NULL);
gtk_widget_show(button);
gtk_table_attach(GTK_TABLE(table), button, col, col + 1, row, row + 1, GTK_FILL, 0, 0, 0);
col++;
@@ -327,18 +327,17 @@ void gradient_update_image(int index)
int w = 70;
int h = 30;
GdkPixmap *pixmap = gdk_pixmap_new(NULL, w, h, 24);
cairo_surface_t *pixmap = cairo_image_surface_create(CAIRO_FORMAT_RGB24, w, h);
cairo_t *cr = gdk_cairo_create(pixmap);
cairo_t *cr = cairo_create(pixmap);
cairo_set_source_rgb(cr, 0.5, 0.5, 0.5);
cairo_rectangle(cr, 0, 0, w, h);
cairo_fill(cr);
gradient_draw(cr, g, w, h, FALSE);
GdkPixbuf *pixbuf = gdk_pixbuf_get_from_drawable(NULL, pixmap, gdk_colormap_get_system(), 0, 0, 0, 0, w, h);
if (pixmap)
g_object_unref(pixmap);
GdkPixbuf *pixbuf = gdk_pixbuf_get_from_surface(pixmap, 0, 0, w, h);
cairo_surface_destroy(pixmap);
GtkTreePath *path;
GtkTreeIter iter;
@@ -528,16 +527,14 @@ void gradient_stop_update_image(int index)
int w = 70;
int h = 30;
GdkPixmap *pixmap = gdk_pixmap_new(NULL, w, h, 24);
cairo_t *cr = gdk_cairo_create(pixmap);
cairo_surface_t *s = cairo_image_surface_create(CAIRO_FORMAT_RGB24, w, h);
cairo_t *cr = cairo_create(s);
cairo_set_source_rgba(cr, stop->color.rgb[0], stop->color.rgb[1], stop->color.rgb[2], stop->color.alpha);
cairo_rectangle(cr, 0, 0, w, h);
cairo_fill(cr);
GdkPixbuf *pixbuf = gdk_pixbuf_get_from_drawable(NULL, pixmap, gdk_colormap_get_system(), 0, 0, 0, 0, w, h);
if (pixmap)
g_object_unref(pixmap);
GdkPixbuf *pixbuf = gdk_pixbuf_get_from_surface(s, 0, 0, w, h);
cairo_surface_destroy(s);
GtkTreePath *path;
GtkTreeIter iter;

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);
}
@@ -383,6 +383,11 @@ static void menuAbout()
// ====== Theme import/copy/delete ======
static void free_data(gpointer data, gpointer userdata)
{
g_free(data);
}
// Shows open dialog and copies the selected files to ~ without overwrite.
static void menuImportFile()
{
@@ -407,7 +412,7 @@ static void menuImportFile()
gchar *newpath = import_no_overwrite(l->data);
g_free(newpath);
}
g_slist_foreach(list, (GFunc)g_free, NULL);
g_slist_foreach(list, free_data, NULL);
g_slist_free(list);
gtk_widget_destroy(dialog);
}

View File

@@ -14,6 +14,27 @@
#define GETTEXT_PACKAGE "tint2conf"
#endif
#ifndef GTK_TYPE_INT
#define GTK_TYPE_INT G_TYPE_INT
#endif
#ifndef GTK_TYPE_STRING
#define GTK_TYPE_STRING G_TYPE_STRING
#endif
#ifndef GTK_TYPE_BOOL
#define GTK_TYPE_BOOL G_TYPE_BOOLEAN
#endif
#ifndef GTK_TYPE_DOUBLE
#define GTK_TYPE_DOUBLE G_TYPE_DOUBLE
#endif
#define gtk_tooltips_set_tip(t, widget, txt, arg) gtk_widget_set_tooltip_text(widget, txt)
#define GTK_OBJECT(x) (x)
#define GTK_SIGNAL_FUNC G_CALLBACK
#define SNAPSHOT_TICK 190
gboolean update_snapshot(gpointer ignored);
void menuApply();

File diff suppressed because it is too large Load Diff

2448
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

@@ -15,3 +15,22 @@ do
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

File diff suppressed because it is too large Load Diff

View File

@@ -9,10 +9,11 @@
#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,
*panel_autohide_size;
extern GtkWidget *panel_wm_menu, *panel_dock, *panel_pivot_struts, *panel_autohide, *panel_autohide_show_time,
*panel_autohide_hide_time, *panel_autohide_size;
extern GtkWidget *panel_combo_strut_policy, *panel_combo_layer, *panel_combo_width_type, *panel_combo_height_type,
*panel_combo_monitor;
extern GtkWidget *panel_window_name, *disable_transparency;
@@ -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)
@@ -286,6 +295,7 @@ void config_write_panel(FILE *fp)
fprintf(fp, "panel_background_id = %d\n", gtk_combo_box_get_active(GTK_COMBO_BOX(panel_background)));
fprintf(fp, "wm_menu = %d\n", gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(panel_wm_menu)) ? 1 : 0);
fprintf(fp, "panel_dock = %d\n", gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(panel_dock)) ? 1 : 0);
fprintf(fp, "panel_pivot_struts = %d\n", gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(panel_pivot_struts)) ? 1 : 0);
fprintf(fp, "panel_position = ");
if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(screen_position[POS_BLH]))) {
@@ -369,6 +379,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");
}
@@ -441,10 +458,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");
@@ -469,7 +488,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);
}
@@ -480,7 +499,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,
@@ -492,7 +511,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)));
}
@@ -517,6 +536,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))) {
@@ -1094,7 +1118,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"))
@@ -1225,6 +1255,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 */
@@ -1349,6 +1385,8 @@ void add_entry(char *key, char *value)
gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(panel_wm_menu), atoi(value));
} else if (strcmp(key, "panel_dock") == 0) {
gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(panel_dock), atoi(value));
} else if (strcmp(key, "panel_pivot_struts") == 0) {
gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(panel_pivot_struts), atoi(value));
} else if (strcmp(key, "panel_layer") == 0) {
if (strcmp(value, "bottom") == 0)
gtk_combo_box_set_active(GTK_COMBO_BOX(panel_combo_layer), 2);
@@ -1526,12 +1564,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) {
@@ -1732,6 +1772,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) {
@@ -1811,10 +1855,12 @@ void add_entry(char *key, char *value)
} else if (strcmp(key, "launcher_apps_dir") == 0) {
char *path = expand_tilde(value);
if (gtk_entry_get_text_length(GTK_ENTRY(launcher_apps_dirs)) > 0) {
gtk_entry_append_text(GTK_ENTRY(launcher_apps_dirs), ",");
int position = gtk_entry_get_text_length(GTK_ENTRY(launcher_apps_dirs));
if (position > 0) {
gtk_editable_insert_text(GTK_EDITABLE(launcher_apps_dirs), ",", 1, &position);
}
gtk_entry_append_text(GTK_ENTRY(launcher_apps_dirs), path);
position = gtk_entry_get_text_length(GTK_ENTRY(launcher_apps_dirs));
gtk_editable_insert_text(GTK_EDITABLE(launcher_apps_dirs), path, strlen(path), &position);
free(path);
} else if (strcmp(key, "launcher_icon_theme") == 0) {

View File

@@ -207,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

@@ -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.,
@@ -915,10 +929,8 @@ void area_compute_text_geometry(Area *area,
const char *line2,
PangoFontDescription *line1_font_desc,
PangoFontDescription *line2_font_desc,
int *line1_height_ink,
int *line1_height,
int *line1_width,
int *line2_height_ink,
int *line2_height,
int *line2_width)
{
@@ -927,7 +939,6 @@ void area_compute_text_geometry(Area *area,
if (line1 && line1[0])
get_text_size2(line1_font_desc,
line1_height_ink,
line1_height,
line1_width,
available_h,
@@ -936,13 +947,14 @@ void area_compute_text_geometry(Area *area,
strlen(line1),
PANGO_WRAP_WORD_CHAR,
PANGO_ELLIPSIZE_NONE,
FALSE);
PANGO_ALIGN_CENTER,
FALSE,
((Panel*)area->panel)->scale);
else
*line1_width = *line1_height_ink = *line1_height = 0;
*line1_width = *line1_height = 0;
if (line2 && line2[0])
get_text_size2(line2_font_desc,
line2_height_ink,
line2_height,
line2_width,
available_h,
@@ -951,9 +963,11 @@ void area_compute_text_geometry(Area *area,
strlen(line2),
PANGO_WRAP_WORD_CHAR,
PANGO_ELLIPSIZE_NONE,
FALSE);
PANGO_ALIGN_CENTER,
FALSE,
((Panel*)area->panel)->scale);
else
*line2_width = *line2_height_ink = *line2_height = 0;
*line2_width = *line2_height = 0;
}
int text_area_compute_desired_size(Area *area,
@@ -962,16 +976,14 @@ int text_area_compute_desired_size(Area *area,
PangoFontDescription *line1_font_desc,
PangoFontDescription *line2_font_desc)
{
int line1_height_ink, line1_height, line1_width, line2_height_ink, line2_height, line2_width;
int line1_height, line1_width, line2_height, line2_width;
area_compute_text_geometry(area,
line1,
line2,
line1_font_desc,
line2_font_desc,
&line1_height_ink,
&line1_height,
&line1_width,
&line2_height_ink,
&line2_height,
&line2_width);
@@ -996,17 +1008,15 @@ gboolean resize_text_area(Area *area,
schedule_redraw(area);
int line1_height_ink, line1_height, line1_width;
int line2_height_ink, line2_height, line2_width;
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_ink,
&line1_height,
&line1_width,
&line2_height_ink,
&line2_height,
&line2_width);
@@ -1053,12 +1063,16 @@ void draw_text_area(Area *area,
PangoFontDescription *line2_font_desc,
int line1_posy,
int line2_posy,
Color *color)
Color *color,
double scale)
{
int inner_w, inner_h;
area_compute_inner_size(area, &inner_w, &inner_h);
PangoLayout *layout = pango_cairo_create_layout(c);
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);
@@ -1070,7 +1084,7 @@ void draw_text_area(Area *area,
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);
draw_text(layout, c, (area->width - inner_w) / 2, line1_posy, color, ((Panel *)area->panel)->font_shadow ? layout : NULL);
}
if (line2 && line2[0]) {
@@ -1078,10 +1092,11 @@ void draw_text_area(Area *area,
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);
draw_text(layout, c, (area->width - inner_w) / 2, line2_posy, color, ((Panel *)area->panel)->font_shadow ? layout : NULL);
}
g_object_unref(layout);
g_object_unref(context);
}
Area *compute_element_area(Area *area, Element element)

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.
@@ -268,10 +273,8 @@ void area_compute_text_geometry(Area *area,
const char *line2,
PangoFontDescription *line1_font_desc,
PangoFontDescription *line2_font_desc,
int *line1_height_ink,
int *line1_height,
int *line1_width,
int *line2_height_ink,
int *line2_height,
int *line2_width);
int text_area_compute_desired_size(Area *area,
@@ -294,7 +297,8 @@ void draw_text_area(Area *area,
PangoFontDescription *line2_font_desc,
int line1_posy,
int line2_posy,
Color *color);
Color *color,
double scale);
int left_border_width(Area *a);
int right_border_width(Area *a);

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

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,7 +31,7 @@
#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>
@@ -48,18 +48,10 @@
#include <librsvg/rsvg.h>
#endif
#ifdef ENABLE_LIBUNWIND
#define UNW_LOCAL_ONLY
#include <libunwind.h>
#else
#ifdef ENABLE_EXECINFO
#include <execinfo.h>
#endif
#endif
#include "../panel.h"
#include "timer.h"
#include "signals.h"
#include "bt.h"
void write_string(int fd, const char *s)
{
@@ -83,39 +75,13 @@ void log_string(int fd, const char *s)
void dump_backtrace(int log_fd)
{
#ifndef DISABLE_BACKTRACE
struct backtrace bt;
get_backtrace(&bt, 1);
log_string(log_fd, "\n" YELLOW "Backtrace:" RESET "\n");
#ifdef ENABLE_LIBUNWIND
unw_cursor_t cursor;
unw_context_t context;
unw_getcontext(&context);
unw_init_local(&cursor, &context);
while (unw_step(&cursor) > 0) {
unw_word_t offset;
char fname[128];
fname[0] = '\0';
(void)unw_get_proc_name(&cursor, fname, sizeof(fname), &offset);
log_string(log_fd, fname);
for (size_t i = 0; i < bt.frame_count; i++) {
log_string(log_fd, bt.frames[i].name);
log_string(log_fd, "\n");
}
#else
#ifdef ENABLE_EXECINFO
#define MAX_TRACE_SIZE 128
void *array[MAX_TRACE_SIZE];
size_t size = backtrace(array, MAX_TRACE_SIZE);
char **strings = backtrace_symbols(array, size);
for (size_t i = 0; i < size; i++) {
log_string(log_fd, strings[i]);
log_string(log_fd, "\n");
}
free(strings);
#endif // ENABLE_EXECINFO
#endif // ENABLE_LIBUNWIND
#endif // DISABLE_BACKTRACE
}
// sleep() returns early when signals arrive. This function does not.
@@ -191,7 +157,7 @@ const char *signal_name(int sig)
return "SIGSYS: Bad system call.";
}
static char s[64];
sprintf(s, "SIG=%d: Unknown", sig);
snprintf(s, sizeof(s), "SIG=%d: Unknown", sig);
return s;
}
@@ -266,7 +232,7 @@ extern char *config_path;
int setenvd(const char *name, const int value)
{
char buf[256];
sprintf(buf, "%d", value);
snprintf(buf, sizeof(buf), "%d", value);
return setenv(name, buf, 1);
}
@@ -396,8 +362,12 @@ pid_t tint_exec(const char *command,
// Allow children to exist after parent destruction
setsid();
// Run the command
if (dir)
chdir(dir);
if (dir) {
int ret = chdir(dir);
if (ret != 0) {
fprintf(stderr, "tint2: failed to chdir to %s\n", dir);
}
}
close_all_fds();
reset_signals();
if (terminal) {
@@ -406,12 +376,14 @@ pid_t tint_exec(const char *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";
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);
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);
@@ -459,9 +431,10 @@ 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);
@@ -474,14 +447,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 {
@@ -753,27 +728,60 @@ void render_image(Drawable d, int x, int y)
XFreePixmap(server.display, pixmap);
}
void draw_text(PangoLayout *layout, cairo_t *c, int posx, int posy, Color *color, int font_shadow)
gboolean is_color_attribute(PangoAttribute *attr, gpointer user_data)
{
if (font_shadow) {
const int shadow_size = 3;
const double shadow_edge_alpha = 0.0;
int i, j;
for (i = -shadow_size; i <= shadow_size; i++) {
for (j = -shadow_size; j <= shadow_size; j++) {
cairo_set_source_rgba(c,
0.0,
0.0,
0.0,
1.0 -
(1.0 - shadow_edge_alpha) *
sqrt((i * i + j * j) / (double)(shadow_size * shadow_size)));
pango_cairo_update_layout(c, layout);
cairo_move_to(c, posx + i, posy + j);
pango_cairo_show_layout(c, layout);
}
return attr->klass->type == PANGO_ATTR_FOREGROUND ||
attr->klass->type == PANGO_ATTR_BACKGROUND ||
attr->klass->type == PANGO_ATTR_UNDERLINE_COLOR ||
attr->klass->type == PANGO_ATTR_STRIKETHROUGH_COLOR ||
attr->klass->type == PANGO_ATTR_FOREGROUND_ALPHA ||
attr->klass->type == PANGO_ATTR_BACKGROUND_ALPHA;
}
gboolean layout_set_markup_strip_colors(PangoLayout *layout, const char *markup)
{
PangoAttrList *attrs = NULL;
char *text = NULL;
GError *error = NULL;
if (!pango_parse_markup(markup, -1, 0, &attrs, &text, NULL, &error)) {
g_error_free(error);
return FALSE;
}
pango_layout_set_text(layout, text, -1);
g_free(text);
pango_attr_list_filter(attrs, is_color_attribute, NULL);
pango_layout_set_attributes(layout, attrs);
pango_attr_list_unref(attrs);
return TRUE;
}
void draw_shadow(cairo_t *c, int posx, int posy, PangoLayout *shadow_layout)
{
const int shadow_size = 3;
const double shadow_edge_alpha = 0.0;
int i, j;
for (i = -shadow_size; i <= shadow_size; i++) {
for (j = -shadow_size; j <= shadow_size; j++) {
cairo_set_source_rgba(c,
0.0,
0.0,
0.0,
1.0 -
(1.0 - shadow_edge_alpha) *
sqrt((i * i + j * j) / (double)(shadow_size * shadow_size)));
pango_cairo_update_layout(c, shadow_layout);
cairo_move_to(c, posx + i, posy + j);
pango_cairo_show_layout(c, shadow_layout);
}
}
}
void draw_text(PangoLayout *layout, cairo_t *c, int posx, int posy, Color *color, PangoLayout *shadow_layout)
{
if (shadow_layout)
draw_shadow(c, posx, posy, shadow_layout);
cairo_set_source_rgba(c, color->rgb[0], color->rgb[1], color->rgb[2], color->alpha);
pango_cairo_update_layout(c, layout);
cairo_move_to(c, posx, posy);
@@ -783,15 +791,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
@@ -808,24 +816,22 @@ Imlib_Image load_image(const char *path, int cached)
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;
}
@@ -919,31 +925,35 @@ void clear_pixmap(Pixmap p, int x, int y, int w, int h)
XRenderFreePicture(server.display, pict);
}
void get_text_size2(const PangoFontDescription *font,
int *height_ink,
int *height,
int *width,
int available_height,
int available_width,
const char *text,
int text_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;
available_width = MAX(0, available_width);
available_height = MAX(0, available_height);
Pixmap pmap = XCreatePixmap(server.display, server.root_win, available_height, available_width, server.depth);
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);
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);
@@ -954,17 +964,65 @@ void get_text_size2(const PangoFontDescription *font,
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;
// 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)
{
@@ -1057,3 +1115,117 @@ GString *tint2_g_string_replace(GString *s, const char *from, const char *to)
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)
@@ -109,6 +106,7 @@ 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);
@@ -116,7 +114,6 @@ void create_heuristic_mask(DATA32 *data, int w, int h);
void render_image(Drawable d, int x, int y);
void get_text_size2(const PangoFontDescription *font,
int *height_ink,
int *height,
int *width,
int available_height,
@@ -125,9 +122,12 @@ void get_text_size2(const PangoFontDescription *font,
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);
gboolean layout_set_markup_strip_colors(PangoLayout *layout, const char *markup);
void draw_text(PangoLayout *layout, cairo_t *c, int posx, int posy, Color *color, PangoLayout *shadow_layout);
// Draws a rounded rectangle
void draw_rect(cairo_t *c, double x, double y, double w, double h, double r);
@@ -150,6 +150,10 @@ 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); \

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

@@ -19,11 +19,11 @@
**************************************************************************/
#include <X11/extensions/Xdamage.h>
#include <X11/extensions/Xrender.h>
#include <X11/extensions/Xrandr.h>
#include <X11/extensions/Xrender.h>
#include <stdio.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
@@ -36,8 +36,9 @@
Server server;
void server_catch_error(Display *d, XErrorEvent *ev)
int server_catch_error(Display *d, XErrorEvent *ev)
{
return 0;
}
void server_init_atoms()
@@ -311,6 +312,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)) {
@@ -339,12 +355,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]);
fprintf(stderr, "tint2: 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);
@@ -357,6 +383,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;
}
}
@@ -393,6 +420,7 @@ 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;
}
}
@@ -523,7 +551,8 @@ int get_current_desktop()
// 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);
// 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));
@@ -648,9 +677,10 @@ void handle_crash(const char *reason)
#endif
}
void x11_io_error(Display *display)
int x11_io_error(Display *display)
{
handle_crash("X11 I/O error");
return 0;
}
#ifdef HAVE_SN

View File

@@ -107,6 +107,7 @@ typedef struct Monitor {
int y;
int width;
int height;
int dpi;
gboolean primary;
gchar **names;
} Monitor;
@@ -147,6 +148,7 @@ typedef struct Server {
Global_atom atom;
int xdamage_event_type;
int xdamage_event_error_type;
gboolean has_shm;
#ifdef HAVE_SN
SnDisplay *sn_display;
GTree *pids;
@@ -162,12 +164,12 @@ void send_event32(Window win, Atom at, long data1, long data2, long data3);
int get_property32(Window win, Atom at, Atom type);
void *server_get_property(Window win, Atom at, Atom type, int *num_results);
Atom server_get_atom(char *atom_name);
void server_catch_error(Display *d, XErrorEvent *ev);
int 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);
int x11_io_error(Display *display);
void handle_crash(const char *reason);
// detect root background

View File

@@ -1,9 +1,11 @@
#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>
@@ -34,6 +36,7 @@ void reset_signals()
sigprocmask(SIG_SETMASK, &signal_set, NULL);
}
#ifndef TINT2CONF
void init_signals()
{
// Set signal handlers
@@ -164,3 +167,4 @@ int get_signal_pending()
{
return signal_pending;
}
#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

File diff suppressed because it is too large Load Diff

View File

@@ -21,45 +21,53 @@
#include <glib.h>
#include <time.h>
#include <sys/time.h>
#include "bool.h"
// Single shot timers (i.e. timers with interval_msec == 0) are deleted automatically as soon as they expire,
// i.e. you do not need to stop them, however it is safe to call stop_timeout for these timers.
// You can pass the address of the variable storing the pointer to the timer as 'self' in add_timeout, in which
// case it is used to clear the pointer if the timer is destroyed automatically. This enforces the timeout pointers
// to be either valid or NULL.
// Periodic timeouts are aligned to each other whenever possible, i.e. one interval_msec is an
// integral multiple of the other.
extern bool warnings_for_timers;
extern bool debug_timers;
typedef struct _timeout timeout;
typedef void TimerCallback(void *arg);
// Initializes default global data.
void default_timeout();
typedef struct {
char name_[64];
bool enabled_;
long long expiration_time_ms_;
int period_ms_;
TimerCallback *callback_;
void *arg_;
bool handled_;
} Timer;
// Cleans up: stops all timers and frees memory.
void cleanup_timeout();
#define DEFAULT_TIMER {"", 0, 0, 0, 0, 0, 0}
// Installs a timer with the first timeout after 'value_msec' and then an optional periodic timeout every
// 'interval_msec' (set it to 0 to prevent periodic timeouts).
// '_callback' is the function called when the timer reaches the timeout.
// 'arg' is the argument passed to the callback function.
// 'self' is an optional pointer to a timeout* variable. If non-NULL, the variable is set to NULL when the timer
// is destroyed (with stop_timeout, cleanup_timeout or when the timer expires and it is single-shot).
// Returns a pointer to the timer, which is needed for stopping/changing it.
timeout *add_timeout(int value_msec, int interval_msec, void (*_callback)(void *), void *arg, timeout **self);
#define INIT_TIMER(t) init_timer(&t, #t)
// Changes timer 't'. If it does not exist, a new timer is created, with self set to 't'.
void change_timeout(timeout **t, int value_msec, int interval_msec, void (*_callback)(void *), void *arg);
// Initialize the timer module.
void default_timers();
// Stops the timer 't'
void stop_timeout(timeout *t);
// Destroy the timer module.
void cleanup_timers();
// Get the time when the next installed timer will expire, or NULL if there is no timer.
// Do not free the pointer; but it is safe to change its contents.
struct timeval *get_next_timeout();
// Initialize a timer. Caller keeps ownership.
void init_timer(Timer *timer, const char *name);
// Callback of all expired timeouts
// Destroy a timer. Does not free() the pointer.
void destroy_timer(Timer *timer);
// Modify a timer.
void change_timer(Timer *timer, bool enabled, int delay_ms, int period_ms, TimerCallback *callback, void *arg);
void stop_timer(Timer *timer);
// Get the time duration to the next expiration time, or NULL if there is no active timer.
// Do not free the pointer; it is harmless to change its contents.
struct timeval *get_duration_to_next_timer_expiration();
// Trigger all expired timers, and reschedule them if they are periodic timers
void handle_expired_timers();
// Time helper functions.
// Returns -1 if t1 < t2, 0 if t1 == t2, 1 if t1 > t2
gint compare_timespecs(const struct timespec *t1, const struct timespec *t2);
@@ -69,6 +77,7 @@ struct timespec add_msec_to_timespec(struct timespec ts, int msec);
// At the first call returns zero.
double profiling_get_time();
// Get current time in seconds, from an unspecified origin.
double get_time();
#endif // TIMER_H

View File

@@ -49,8 +49,9 @@ char *addr2name(void *func)
free(strings);
return result;
#else
char *result = (char*) calloc(32, 1);
sprintf(result, "%p", func);
const size_t buf_size = 32;
char *result = (char*) calloc(buf_size, 1);
snprintf(result, buf_size, "%p", func);
return result;
#endif
}

View File

@@ -24,11 +24,17 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdint.h>
#include <sys/types.h>
#include <Imlib2.h>
#include <cairo.h>
#include <cairo-xlib.h>
#include <X11/extensions/XShm.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include "common.h"
#include "window.h"
#include "server.h"
@@ -157,7 +163,8 @@ int get_window_desktop(Window win)
if (best_match < 0)
best_match = 0;
// fprintf(stderr, "tint2: window %lx %s : viewport %d, (%d, %d)\n", win, get_task(win) ? get_task(win)->title : "??",
// fprintf(stderr, "tint2: window %lx %s : viewport %d, (%d, %d)\n", win, get_task(win) ? get_task(win)->title :
// "??",
// best_match+1, x, y);
return best_match;
}
@@ -190,15 +197,18 @@ int get_window_monitor(Window win)
return best_match;
}
void get_window_coordinates(Window win, int *x, int *y, int *w, int *h)
gboolean get_window_coordinates(Window win, int *x, int *y, int *w, int *h)
{
int dummy_int;
unsigned ww, wh, bw, bh;
Window src;
XTranslateCoordinates(server.display, win, server.root_win, 0, 0, x, y, &src);
XGetGeometry(server.display, win, &src, &dummy_int, &dummy_int, &ww, &wh, &bw, &bh);
if (!XTranslateCoordinates(server.display, win, server.root_win, 0, 0, x, y, &src))
return FALSE;
if (!XGetGeometry(server.display, win, &src, &dummy_int, &dummy_int, &ww, &wh, &bw, &bh))
return FALSE;
*w = ww + bw;
*h = wh + bh;
*h = wh + bw;
return TRUE;
}
gboolean window_is_iconified(Window win)
@@ -352,3 +362,293 @@ char *get_window_name(Window win)
XFree(text_property.value);
return result;
}
void smooth_thumbnail(cairo_surface_t *image_surface)
{
u_int32_t *data = (u_int32_t *)cairo_image_surface_get_data(image_surface);
const size_t tw = cairo_image_surface_get_width(image_surface);
const size_t th = cairo_image_surface_get_height(image_surface);
const size_t rmask = 0xff0000;
const size_t gmask = 0xff00;
const size_t bmask = 0xff;
for (size_t i = 0; i < tw * (th - 1) - 1 && i < tw * th; i++) {
u_int32_t c1 = data[i];
u_int32_t c2 = data[i + 1];
u_int32_t c3 = data[i + tw];
u_int32_t c4 = data[i + tw + 1];
u_int32_t b = (5 * (c1 & bmask) + 1 * (c2 & bmask) + 1 * (c3 & bmask) + 1 * (c4 & bmask)) / 8;
u_int32_t g = (5 * (c1 & gmask) + 1 * (c2 & gmask) + 1 * (c3 & gmask) + 1 * (c4 & gmask)) / 8;
u_int32_t r = (5 * (c1 & rmask) + 1 * (c2 & rmask) + 1 * (c3 & rmask) + 1 * (c4 & rmask)) / 8;
data[i] = (r & rmask) | (g & gmask) | (b & bmask);
}
}
// This is measured to be slightly faster.
#define GetPixel(ximg, x, y) ((u_int32_t *)&(ximg->data[y * ximg->bytes_per_line]))[x]
//#define GetPixel XGetPixel
cairo_surface_t *get_window_thumbnail_ximage(Window win, size_t size, gboolean use_shm)
{
cairo_surface_t *result = NULL;
XWindowAttributes wa = {};
if (!XGetWindowAttributes(server.display, win, &wa) || wa.width <= 0 || wa.height <= 0 ||
wa.map_state != IsViewable) {
if (debug_thumbnails) {
fprintf(stderr, "tint2: could not get thumbnail, invalid geometry %d x %d\n",
wa.width, wa.height);
}
goto err0;
}
if (window_is_iconified(win)) {
if (debug_thumbnails) {
fprintf(stderr, "tint2: could not get thumbnail, minimized window\n");
}
goto err0;
}
if (debug_thumbnails) {
fprintf(stderr, "tint2: getting thumbnail for window with size %d x %d\n",
wa.width, wa.height);
}
size_t w, h;
w = (size_t)wa.width;
h = (size_t)wa.height;
size_t tw, th, fw;
size_t ox, oy;
tw = size;
th = h * tw / w;
if (th > tw * 0.618) {
th = (size_t)(tw * 0.618);
fw = w * th / h;
ox = (tw - fw) / 2;
oy = 0;
} else {
fw = tw;
ox = oy = 0;
}
if (debug_thumbnails) {
fprintf(stderr,
"tint2: thumbnail size %zu x %zu, "
"proportional width %zu, offset %zu\n",
tw, th, fw, ox);
}
if (!w || !h || !tw || !th || !fw) {
if (debug_thumbnails) {
fprintf(stderr, "tint2: could not get thumbnail, invalid thumbnail size: "
"%zu x %zu => %zu x %zu, %zu\n",
w, h, tw, th, fw);
}
goto err0;
}
XShmSegmentInfo shminfo;
XImage *ximg;
if (use_shm)
ximg = XShmCreateImage(server.display,
wa.visual,
(unsigned)wa.depth,
ZPixmap,
NULL,
&shminfo,
(unsigned)w,
(unsigned)h);
else
ximg = XGetImage(server.display, win, 0, 0, (unsigned)w, (unsigned)h, AllPlanes, ZPixmap);
if (!ximg) {
fprintf(stderr, RED "tint2: !ximg" RESET "\n");
goto err0;
}
if (ximg->bits_per_pixel != 24 && ximg->bits_per_pixel != 32) {
fprintf(stderr, RED "tint2: unusual bits_per_pixel" RESET "\n");
goto err1;
}
if (use_shm) {
shminfo.shmid = shmget(IPC_PRIVATE, (size_t)(ximg->bytes_per_line * ximg->height), IPC_CREAT | 0777);
if (shminfo.shmid < 0) {
fprintf(stderr, RED "tint2: !shmget" RESET "\n");
goto err1;
}
shminfo.shmaddr = ximg->data = (char *)shmat(shminfo.shmid, 0, 0);
if (shminfo.shmaddr == (void*)-1) {
fprintf(stderr, RED "tint2: !shmat" RESET "\n");
goto err2;
}
shminfo.readOnly = False;
if (!XShmAttach(server.display, &shminfo)) {
fprintf(stderr, RED "tint2: !xshmattach" RESET "\n");
goto err3;
}
if (!XShmGetImage(server.display, win, ximg, 0, 0, AllPlanes)) {
fprintf(stderr, RED "tint2: !xshmgetimage" RESET "\n");
goto err4;
}
}
XGetWindowAttributes(server.display, win, &wa);
if (wa.map_state != IsViewable) {
if (debug_thumbnails) {
fprintf(stderr, "tint2: could not get thumbnail, window not viewable\n");
}
goto err4;
}
if (debug_thumbnails) {
fprintf(stderr,
"tint2: creating cairo surface with size %zu x %zu = %zu px\n",
tw, th, tw * th);
}
result = cairo_image_surface_create(CAIRO_FORMAT_RGB24, (int)tw, (int)th);
u_int32_t *data = (u_int32_t *)cairo_image_surface_get_data(result);
memset(data, 0, tw * th);
// Fixed-point precision
const size_t prec = 1 << 16;
const size_t xstep = w * prec / fw;
const size_t ystep = h * prec / th;
const size_t offset_y1 = 0 * ystep / 8;
const size_t offset_x1 = 3 * xstep / 8;
const size_t offset_y2 = 1 * ystep / 8;
const size_t offset_x2 = 6 * xstep / 8;
const size_t offset_y3 = 4 * ystep / 8;
const size_t offset_x3 = 2 * xstep / 8;
const size_t offset_y4 = 4 * ystep / 8;
const size_t offset_x4 = 4 * xstep / 8;
const size_t offset_y5 = 4 * ystep / 8;
const size_t offset_x5 = 7 * xstep / 8;
const size_t offset_y6 = 6 * ystep / 8;
const size_t offset_x6 = 1 * xstep / 8;
const size_t offset_y7 = 7 * ystep / 8;
const size_t offset_x7 = 6 * xstep / 8;
const u_int32_t rmask = (u_int32_t)ximg->red_mask;
const u_int32_t gmask = (u_int32_t)ximg->green_mask;
const u_int32_t bmask = (u_int32_t)ximg->blue_mask;
for (size_t yt = 0, y = 0; yt < th; yt++, y += ystep) {
for (size_t xt = 0, x = 0; xt < fw; xt++, x += xstep) {
size_t j = yt * tw + ox + xt;
if (j < tw * th) {
u_int32_t c1 = (u_int32_t)GetPixel(ximg, (int)((x + offset_x1) / prec), (int)((y + offset_y1) / prec));
u_int32_t c2 = (u_int32_t)GetPixel(ximg, (int)((x + offset_x2) / prec), (int)((y + offset_y2) / prec));
u_int32_t c3 = (u_int32_t)GetPixel(ximg, (int)((x + offset_x3) / prec), (int)((y + offset_y3) / prec));
u_int32_t c4 = (u_int32_t)GetPixel(ximg, (int)((x + offset_x4) / prec), (int)((y + offset_y4) / prec));
u_int32_t c5 = (u_int32_t)GetPixel(ximg, (int)((x + offset_x5) / prec), (int)((y + offset_y5) / prec));
u_int32_t c6 = (u_int32_t)GetPixel(ximg, (int)((x + offset_x6) / prec), (int)((y + offset_y6) / prec));
u_int32_t c7 = (u_int32_t)GetPixel(ximg, (int)((x + offset_x7) / prec), (int)((y + offset_y7) / prec));
u_int32_t b = ((c1 & bmask) + (c2 & bmask) + (c3 & bmask) + (c4 & bmask) + (c5 & bmask) * 2 + (c6 & bmask) +
(c7 & bmask)) /
8;
u_int32_t g = ((c1 & gmask) + (c2 & gmask) + (c3 & gmask) + (c4 & gmask) + (c5 & gmask) * 2 + (c6 & gmask) +
(c7 & gmask)) /
8;
u_int32_t r = ((c1 & rmask) + (c2 & rmask) + (c3 & rmask) + (c4 & rmask) + (c5 & rmask) * 2 + (c6 & rmask) +
(c7 & rmask)) /
8;
data[j] = (r & rmask) | (g & gmask) | (b & bmask);
}
}
}
// Convert to argb32
if (rmask & 0xff0000) {
// argb32 or rgb24 => Nothing to do
} else if (rmask & 0xff) {
// bgr24
for (size_t i = 0; i < tw * th; i++) {
u_int32_t r = (data[i] & rmask) << 16;
u_int32_t g = (data[i] & gmask);
u_int32_t b = (data[i] & bmask) >> 16;
data[i] = (r & 0xff0000) | (g & 0x00ff00) | (b & 0x0000ff);
}
} else if (rmask & 0xff00) {
// bgra32
for (size_t i = 0; i < tw * th; i++) {
u_int32_t r = (data[i] & rmask) << 8;
u_int32_t g = (data[i] & gmask) >> 8;
u_int32_t b = (data[i] & bmask) >> 24;
data[i] = (r & 0xff0000) | (g & 0x00ff00) | (b & 0x0000ff);
}
}
// 2nd pass
smooth_thumbnail(result);
cairo_surface_mark_dirty(result);
if (ximg) {
XDestroyImage(ximg);
ximg = NULL;
}
err4:
if (use_shm)
XShmDetach(server.display, &shminfo);
err3:
if (use_shm)
shmdt(shminfo.shmaddr);
err2:
if (use_shm)
shmctl(shminfo.shmid, IPC_RMID, NULL);
err1:
if (ximg)
XDestroyImage(ximg);
err0:
return result;
}
gboolean cairo_surface_is_blank(cairo_surface_t *image_surface)
{
uint32_t *pixels = (uint32_t *)cairo_image_surface_get_data(image_surface);
gboolean empty = TRUE;
int size = cairo_image_surface_get_width(image_surface) * cairo_image_surface_get_height(image_surface);
for (int i = 0; empty && i < size; i++) {
if (pixels[i] & 0xffFFff)
empty = FALSE;
}
return empty;
}
gboolean thumb_use_shm = FALSE;
cairo_surface_t *get_window_thumbnail(Window win, int size)
{
cairo_surface_t *image_surface = NULL;
if (thumb_use_shm && server.has_shm && server.composite_manager) {
image_surface = get_window_thumbnail_ximage(win, (size_t)size, TRUE);
if (image_surface && cairo_surface_is_blank(image_surface)) {
cairo_surface_destroy(image_surface);
image_surface = NULL;
}
if (debug_thumbnails) {
if (!image_surface)
fprintf(stderr, YELLOW "tint2: XShmGetImage failed, trying slower method" RESET "\n");
else
fprintf(stderr, "tint2: captured window using XShmGetImage\n");
}
}
if (!image_surface) {
image_surface = get_window_thumbnail_ximage(win, (size_t)size, FALSE);
if (image_surface && cairo_surface_is_blank(image_surface)) {
cairo_surface_destroy(image_surface);
image_surface = NULL;
}
if (debug_thumbnails) {
if (!image_surface)
fprintf(stderr, YELLOW "tint2: XGetImage failed, trying slower method" RESET "\n");
else
fprintf(stderr, "tint2: captured window using XGetImage\n");
}
}
if (!image_surface)
return NULL;
return image_surface;
}

View File

@@ -25,7 +25,7 @@ int get_window_monitor(Window win);
void activate_window(Window win);
void close_window(Window win);
void get_window_coordinates(Window win, int *x, int *y, int *w, int *h);
gboolean get_window_coordinates(Window win, int *x, int *y, int *w, int *h);
void toggle_window_maximized(Window win);
void toggle_window_shade(Window win);
void change_window_desktop(Window win, int desktop);
@@ -34,5 +34,6 @@ int get_icon_count(gulong *data, int num);
gulong *get_best_icon(gulong *data, int icon_count, int num, int *iw, int *ih, int best_icon_size);
char *get_window_name(Window win);
cairo_surface_t *get_window_thumbnail(Window win, int size);
#endif

107
test/fabfile.py vendored Executable file
View File

@@ -0,0 +1,107 @@
#!/usr/bin/env python2
# Setup: add tint2-runner, tint2-freebsd and tint2-openbsd in /etc/hosts.
# Run: pip install fabric; pip install fabtools.
# TODO: setup bsd workers
# TODO: prin ssh public key to be added on gitlab
from fabric.api import *
from fabric.contrib.files import *
from fabtools import require
import fabtools
import os
env.use_ssh_config = True
env.user = 'root'
env.sudo_prefix += '-H '
env.roledefs = {
'runner': ['tint2-runner'],
'freebsd': ['tint2-freebsd'],
'openbsd': ['tint2-openbsd'],
}
def str2hex(s):
return ''.join('{:02x}'.format(ord(c)) for c in s)
def generate_random_password():
return str2hex(os.urandom(32))
def read_file(path):
with open(path) as f:
return f.read()
@task
@roles('runner', 'freebsd', 'openbsd')
def create_users():
require.user('root', password=generate_random_password())
require.user('runner', password=generate_random_password())
sudo('cd; mkdir -p .ssh; chmod 700 .ssh', user='runner')
if not exists('/home/runner/.ssh/id_rsa'):
sudo('cd; ssh-keygen -f ~/.ssh/id_rsa -t rsa -N ""', user='runner')
@task
@roles('runner')
def install_deps():
require.deb.packages([
# Repo deps
'git',
# Build deps
'build-essential',
'cmake',
'libglib2.0-dev',
'libcairo2-dev',
'libglib2.0-dev',
'libgtk2.0-dev',
'libimlib2-dev',
'libpango1.0-dev',
'librsvg2-dev',
'libstartup-notification0-dev',
'libx11-dev',
'libxcomposite-dev',
'libxdamage-dev',
'libxinerama-dev',
'libxrandr-dev',
'libxrender-dev',
# Tester deps
'python-minimal',
'xvfb',
'xsettingsd',
'openbox',
'compton',
'x11-utils',
'gnome-calculator'
])
@task
@roles('runner')
def pull_code():
if not exists('/home/runner/tint2'):
sudo('cd; git clone https://gitlab.com/o9000/tint2.git', user='runner')
if not exists('/home/runner/tint2.wiki'):
sudo('cd; git clone git@gitlab.com:o9000/tint2.wiki.git', user='runner')
sudo('cd; git config --global user.name "tint2.runner"', user='runner')
sudo('cd; git config --global user.email "tint2.runner@netperf.tools"', user='runner')
@task
@roles('runner')
def add_cron_jobs():
fabtools.cron.add_task('tests', '* * * * *', 'runner', '/home/runner/tint2/test/update_test_status.sh')
fabtools.cron.add_task('packaging_check', '10 */2 * * *', 'runner', '/home/runner/tint2/packaging/update_version_status.sh')
@task(default=True)
@roles('runner')
def full_runner():
create_users()
install_deps()
pull_code()
add_cron_jobs()

View File

@@ -10,6 +10,7 @@ sys.setdefaultencoding('utf8')
import argparse
import datetime
import os
import re
import signal
import subprocess
import time
@@ -39,6 +40,10 @@ def print_err(*args, **kwargs):
print(*args, file=sys.stderr, **kwargs)
def clear_ansi_codes(s):
return re.sub(r"\x1B\[[0-9;]*[a-zA-Z]", "", s)
def run(cmd, output=False):
return subprocess.Popen(cmd,
stdin=devnull,
@@ -160,7 +165,7 @@ def test(tint2path, config, use_asan):
start_wm()
sleep(1)
os.environ["DEBUG_FPS"] = "1"
os.environ["ASAN_OPTIONS"] = "detect_leaks=1"
os.environ["ASAN_OPTIONS"] = "detect_leaks=1:exitcode=0"
tint2 = run([tint2path, "-c", config], True)
if tint2.poll() != None:
raise RuntimeError("tint2 failed to start")
@@ -188,6 +193,7 @@ def test(tint2path, config, use_asan):
mem, mem_detail = get_mem_usage(tint2.pid)
stop(tint2)
out, _ = tint2.communicate()
out = clear_ansi_codes(out)
exitcode = tint2.returncode
if exitcode != 0 and exitcode != 23:
print("tint2 crashed with exit code {0}!".format(exitcode))
@@ -214,7 +220,7 @@ def test(tint2path, config, use_asan):
if use_asan:
fps_status = ok
else:
fps_status = ok if min_fps > 60 else warning if min_fps > 40 else error
fps_status = ok if min_fps > 30 else warning if min_fps > 20 else error
print("FPS:", "min:", min_fps, "median:", med_fps, fps_status)
if mem_status != ok or leak_status != ok or fps_status != ok:
print("Output:")
@@ -222,6 +228,41 @@ def test(tint2path, config, use_asan):
stop_xvfb()
def run_unit_tests(tint2path, use_asan):
print("# Unit tests", "(ASAN on)" if use_asan else "")
start_xvfb()
sleep(1)
start_xsettings()
start_wm()
sleep(1)
compton = start_compositor()
sleep(1)
os.environ["DEBUG_FPS"] = "1"
os.environ["ASAN_OPTIONS"] = "detect_leaks=1:exitcode=0"
tint2 = run([tint2path, "--test-verbose"], True)
time_limit = time.time() + 60
while tint2.poll() == None:
if time.time() < time_limit:
time.sleep(1)
continue
tint2.stop()
out, _ = tint2.communicate()
out = clear_ansi_codes(out)
exitcode = tint2.returncode
if exitcode != 0 and exitcode != 23:
print("tint2 crashed with exit code {0}!".format(exitcode))
print("Output:")
print("```\n" + out.strip() + "\n```")
return
if "tests succeeded" in out:
num_tests = [line for line in out.split("\n") if "tint2: Running" in line][0]
print("All {0} tests succeeded.".format(num_tests))
return
if "tests failed" in out:
print("```\n" + out.strip() + "\n```")
stop_xvfb()
def show_timestamp():
utc_datetime = datetime.datetime.utcnow()
print("Last updated:", utc_datetime.strftime("%Y-%m-%d %H:%M UTC"))
@@ -285,6 +326,29 @@ def compile_and_report(src_dir, use_asan):
print("Status: Succeeded in %.1f seconds" % (duration,), ok)
def compile_remotely_and_report(host):
print_err("Compiling on {0}...".format(host))
print("# Compilation on {0}".format(host))
start = time.time()
c = run("ssh worker@{0} 'cd tint2 && git pull && mkdir -p build && rm -rf build && mkdir -p build && cd build && cmake .. && make && ./tint2 --version'".format(host), True)
out, _ = c.communicate()
duration = time.time() - start
if c.returncode != 0:
print("Status: Failed!", error)
print("Output:")
print("```\n" + out.strip() + "\n```")
warnings = [ line for line in out.split("\n") if "warning:" in line and ".so." not in line.split(":", 1)[0] ]
if warnings:
print("Status: Succeeded with warnings!", warning)
print("Warnings:")
print("```", end="")
for line in warnings:
print(line, end="")
print("```", end="")
else:
print("Status: Succeeded in %.1f seconds" % (duration,), ok)
def run_test(config, index, use_asan):
print_err("Running test", index, "for config", config)
print("# Test", index, "(ASAN on)" if use_asan else "")
@@ -350,8 +414,11 @@ def main():
show_timestamp()
show_git_info(args.src_dir)
show_system_info()
compile_remotely_and_report("FreeBSD")
compile_remotely_and_report("OpenBSD")
for use_asan in [True, False]:
compile_and_report(args.src_dir, use_asan)
run_unit_tests("./build/tint2", use_asan)
run_tests(use_asan)

View File

@@ -1,5 +1,7 @@
#!/bin/bash
export PATH=/usr/local/bin:/usr/bin:/bin:/usr/local/sbin:/usr/sbin:/sbin
set -e
set -x
@@ -11,7 +13,7 @@ exec 2>&1
cd ~/tint2
git reset --hard
git pull
last=$(cat .last-reg-test)
last=$(cat .last-reg-test || true)
curr=$(git rev-parse --verify HEAD)
[ "$last" == "$curr" ] && exit 0

View File

@@ -201,7 +201,7 @@ clock_dwheel_command =
# Battery
battery_tooltip = 1
battery_low_status = 10
battery_low_cmd = notify-send "battery low"
battery_low_cmd = xmessage 'tint2: Battery low!'
battery_full_cmd =
bat1_font = sans 8
bat2_font = sans 6

View File

@@ -201,7 +201,7 @@ clock_dwheel_command =
# Battery
battery_tooltip = 1
battery_low_status = 10
battery_low_cmd = notify-send "battery low"
battery_low_cmd = xmessage 'tint2: Battery low!'
battery_full_cmd =
bat1_font = sans 8
bat2_font = sans 6

View File

@@ -212,7 +212,7 @@ clock_dwheel_command =
# Battery
battery_tooltip = 1
battery_low_status = 10
battery_low_cmd = notify-send "battery low"
battery_low_cmd = xmessage 'tint2: Battery low!'
battery_full_cmd =
bat1_font = sans 8
bat2_font = sans 6

View File

@@ -201,7 +201,7 @@ clock_dwheel_command =
# Battery
battery_tooltip = 1
battery_low_status = 10
battery_low_cmd = notify-send "battery low"
battery_low_cmd = xmessage 'tint2: Battery low!'
battery_full_cmd =
bat1_font = sans 8
bat2_font = sans 6

View File

@@ -1,4 +1,4 @@
#---- Generated by tint2conf 20b6 ----
#---- Generated by tint2conf bb4a ----
# See https://gitlab.com/o9000/tint2/wikis/Configure for
# full documentation of the configuration options.
#-------------------------------------
@@ -15,6 +15,8 @@ color_stop = 70.000000 #1c1c1c 80
rounded = 0
border_width = 1
border_sides = T
border_content_tint_weight = 0
background_content_tint_weight = 0
background_color = #000000 80
border_color = #333333 80
gradient_id = 1
@@ -27,28 +29,34 @@ border_color_pressed = #555555 80
rounded = 0
border_width = 2
border_sides = B
border_content_tint_weight = 100
background_content_tint_weight = 25
background_color = #777777 0
border_color = #777777 0
background_color_hover = #777777 21
border_color = #777777 100
background_color_hover = #464646 100
border_color_hover = #cccccc 30
background_color_pressed = #5a5a5a 21
background_color_pressed = #1e1e1e 100
border_color_pressed = #777777 30
# Background 3: Active task
rounded = 0
border_width = 2
border_sides = B
background_color = #ffffff 0
border_color = #4d75ff 100
background_color_hover = #ffffff 21
border_color_hover = #4c73ff 100
background_color_pressed = #989898 21
border_color_pressed = #4c73ff 100
border_content_tint_weight = 100
background_content_tint_weight = 100
background_color = #ffffff 100
border_color = #d9d9d9 100
background_color_hover = #ffffff 73
border_color_hover = #d9d9d9 100
background_color_pressed = #989898 73
border_color_pressed = #d9d9d9 100
# Background 4: Urgent task
rounded = 0
border_width = 0
border_sides = TBLR
border_content_tint_weight = 0
background_content_tint_weight = 0
background_color = #aa4400 100
border_color = #aa7733 100
background_color_hover = #aa4400 100
@@ -60,6 +68,8 @@ border_color_pressed = #aa7733 100
rounded = 2
border_width = 1
border_sides = TBLR
border_content_tint_weight = 0
background_content_tint_weight = 0
background_color = #ffffaa 100
border_color = #999999 100
background_color_hover = #ffffaa 100
@@ -71,6 +81,8 @@ border_color_pressed = #999999 100
rounded = 0
border_width = 2
border_sides = B
border_content_tint_weight = 0
background_content_tint_weight = 0
background_color = #777777 0
border_color = #777777 0
background_color_hover = #bdbdbd 21
@@ -82,12 +94,14 @@ border_color_pressed = #777777 100
rounded = 0
border_width = 2
border_sides = B
border_content_tint_weight = 0
background_content_tint_weight = 0
background_color = #ffffff 21
border_color = #4c73ff 100
border_color = #d9d9d9 100
background_color_hover = #ffffff 21
border_color_hover = #4d73ff 100
border_color_hover = #d9d9d9 100
background_color_pressed = #a9a9a9 21
border_color_pressed = #4d73ff 100
border_color_pressed = #d9d9d9 100
#-------------------------------------
# Panel
@@ -146,6 +160,8 @@ task_maximum_size = 120 35
task_padding = 4 3 4
task_font = Sans 8
task_tooltip = 1
task_thumbnail = 0
task_thumbnail_size = 210
task_font_color = #c6c6c6 100
task_active_font_color = #ffffff 100
task_icon_asb = 100 0 0

View File

@@ -1,11 +1,14 @@
#---- Generated by tint2conf 2641 ----
#---- Generated by tint2conf aeaf ----
# See https://gitlab.com/o9000/tint2/wikis/Configure for
# full documentation of the configuration options.
#-------------------------------------
# Gradients
#-------------------------------------
# Backgrounds
# Background 1: Panel
rounded = 0
border_width = 0
border_sides = TBLR
background_color = #000000 60
border_color = #000000 30
background_color_hover = #000000 60
@@ -16,6 +19,7 @@ border_color_pressed = #000000 30
# Background 2: Default task, Iconified task
rounded = 4
border_width = 1
border_sides = TBLR
background_color = #777777 20
border_color = #777777 30
background_color_hover = #aaaaaa 22
@@ -26,6 +30,7 @@ border_color_pressed = #eaeaea 44
# Background 3: Active task
rounded = 4
border_width = 1
border_sides = TBLR
background_color = #777777 20
border_color = #ffffff 40
background_color_hover = #aaaaaa 22
@@ -36,6 +41,7 @@ border_color_pressed = #eaeaea 44
# Background 4: Urgent task
rounded = 4
border_width = 1
border_sides = TBLR
background_color = #aa4400 100
border_color = #aa7733 100
background_color_hover = #cc7700 100
@@ -46,8 +52,9 @@ border_color_pressed = #aa7733 100
# Background 5: Tooltip
rounded = 1
border_width = 1
background_color = #ffffaa 100
border_color = #000000 100
border_sides = TBLR
background_color = #222222 100
border_color = #333333 100
background_color_hover = #ffffaa 100
border_color_hover = #000000 100
background_color_pressed = #ffffaa 100
@@ -65,7 +72,7 @@ panel_dock = 0
panel_position = bottom center horizontal
panel_layer = top
panel_monitor = all
primary_monitor_first = 0
panel_shrink = 0
autohide = 0
autohide_show_timeout = 0
autohide_hide_timeout = 0.5
@@ -81,12 +88,14 @@ mouse_pressed_icon_asb = 100 0 0
#-------------------------------------
# Taskbar
taskbar_mode = single_desktop
taskbar_hide_if_empty = 0
taskbar_padding = 0 0 2
taskbar_background_id = 0
taskbar_active_background_id = 0
taskbar_name = 1
taskbar_hide_inactive_tasks = 0
taskbar_hide_different_monitor = 0
taskbar_hide_different_desktop = 0
taskbar_always_show_all_desktop_tasks = 0
taskbar_name_padding = 4 2
taskbar_name_background_id = 0
@@ -106,6 +115,8 @@ urgent_nb_of_blink = 100000
task_maximum_size = 150 35
task_padding = 2 2 4
task_tooltip = 1
task_thumbnail = 0
task_thumbnail_size = 210
task_font_color = #ffffff 100
task_background_id = 2
task_active_background_id = 3
@@ -125,6 +136,7 @@ systray_sort = ascending
systray_icon_size = 24
systray_icon_asb = 100 0 0
systray_monitor = 1
systray_name_filter =
#-------------------------------------
# Launcher
@@ -163,8 +175,11 @@ clock_dwheel_command =
# Battery
battery_tooltip = 1
battery_low_status = 10
battery_low_cmd = notify-send "battery low"
battery_low_cmd = xmessage 'tint2: Battery low!'
battery_full_cmd =
battery_font_color = #ffffff 100
bat1_format =
bat2_format =
battery_padding = 1 0
battery_background_id = 0
battery_hide = 101
@@ -180,7 +195,7 @@ ac_disconnected_cmd =
# Tooltip
tooltip_show_timeout = 0.5
tooltip_hide_timeout = 0.1
tooltip_padding = 2 2
tooltip_padding = 4 4
tooltip_background_id = 5
tooltip_font_color = #222222 100
tooltip_font_color = #dddddd 100

View File

@@ -201,7 +201,7 @@ clock_dwheel_command =
# Battery
battery_tooltip = 1
battery_low_status = 10
battery_low_cmd = notify-send "battery low"
battery_low_cmd = xmessage 'tint2: Battery low!'
battery_full_cmd =
bat1_font = sans 8
bat2_font = sans 6

View File

@@ -201,7 +201,7 @@ clock_dwheel_command =
# Battery
battery_tooltip = 1
battery_low_status = 10
battery_low_cmd = notify-send "battery low"
battery_low_cmd = xmessage 'tint2: Battery low!'
battery_full_cmd =
bat1_font = sans 8
bat2_font = sans 6

View File

@@ -212,7 +212,7 @@ clock_dwheel_command =
# Battery
battery_tooltip = 1
battery_low_status = 10
battery_low_cmd = notify-send "battery low"
battery_low_cmd = xmessage 'tint2: Battery low!'
battery_full_cmd =
bat1_font = sans 8
bat2_font = sans 6

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