Compare commits
303 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8242b494bc | ||
|
|
1c6377f570 | ||
|
|
dd1fd28114 | ||
|
|
b4610fcb6e | ||
|
|
eb0e6765f9 | ||
|
|
5ee278d3ca | ||
|
|
fb438031c3 | ||
|
|
e5380f8e29 | ||
|
|
c064ec70ed | ||
|
|
a9a9a753bc | ||
|
|
38bee65b58 | ||
|
|
f64cf199e4 | ||
|
|
0911dcaed1 | ||
|
|
c41d75e54e | ||
|
|
8e0bdcaedd | ||
|
|
be4554b89d | ||
|
|
82d7fdc8f9 | ||
|
|
e046cb88ab | ||
|
|
300ef518cb | ||
|
|
457d51e267 | ||
|
|
1b48efe738 | ||
|
|
40e01e2abf | ||
|
|
a7ca1b739b | ||
|
|
750cbd572c | ||
|
|
75c2a2084d | ||
|
|
9b17461f74 | ||
|
|
a185f625f9 | ||
|
|
3a9181eff5 | ||
|
|
5cea67171b | ||
|
|
328a35f949 | ||
|
|
cab9c3bddd | ||
|
|
5dd814773a | ||
|
|
f4384b786c | ||
|
|
5bc83561e0 | ||
|
|
375e965a3a | ||
|
|
67e25b8102 | ||
|
|
c96201930b | ||
|
|
6bf72a030a | ||
|
|
63d0d98a5c | ||
|
|
e739023529 | ||
|
|
a7a9c5cdae | ||
|
|
50822bd2fd | ||
|
|
00c79073f0 | ||
|
|
f5b36b37b6 | ||
|
|
c635f46439 | ||
|
|
16a359f944 | ||
|
|
9a972f4c25 | ||
|
|
9d06ac0157 | ||
|
|
cfac6a645d | ||
|
|
648c7c109f | ||
|
|
8946f93254 | ||
|
|
58e030de5d | ||
|
|
c2f8c210f8 | ||
|
|
978e1c90fc | ||
|
|
3fba8aa1cf | ||
|
|
26251849c6 | ||
|
|
abeb7ce2e6 | ||
|
|
abdb1aeff9 | ||
|
|
7e383c395e | ||
|
|
c3ed2dadf3 | ||
|
|
bd2ca94ffe | ||
|
|
1753641fc9 | ||
|
|
467ea1332c | ||
|
|
68c2ad7062 | ||
|
|
cc5842463d | ||
|
|
5e124c7a97 | ||
|
|
07865142b2 | ||
|
|
0c71fda5e1 | ||
|
|
8eaf187984 | ||
|
|
abe8a0eeb1 | ||
|
|
d82d782541 | ||
|
|
715eb556da | ||
|
|
b9e64da9da | ||
|
|
224b7fba82 | ||
|
|
d72fff9653 | ||
|
|
67c3e47414 | ||
|
|
e96e7fbee7 | ||
|
|
65c91667f9 | ||
|
|
9f4087b471 | ||
|
|
247687307b | ||
|
|
2c9d1fdf7d | ||
|
|
500b8f5bea | ||
|
|
7e6f7df55e | ||
|
|
47201cab84 | ||
|
|
f7d083904f | ||
|
|
f11d30f076 | ||
|
|
c0eaa8274f | ||
|
|
38488b8d75 | ||
|
|
07339c09a0 | ||
|
|
0521223899 | ||
|
|
cbf3cebbb0 | ||
|
|
4b50446a7b | ||
|
|
e8a6c93b28 | ||
|
|
5e6e1184fe | ||
|
|
89ab1fa6c4 | ||
|
|
e597973cd7 | ||
|
|
ebe30774ac | ||
|
|
d463dcb5b4 | ||
|
|
23782a4414 | ||
|
|
1be85e66fe | ||
|
|
2fe7efd4fe | ||
|
|
1b6fd91611 | ||
|
|
5730725762 | ||
|
|
87da8c76cc | ||
|
|
e5ecc0c15d | ||
|
|
812e306376 | ||
|
|
3155a5fc89 | ||
|
|
bc4af51e82 | ||
|
|
01de174919 | ||
|
|
7ddb373cb4 | ||
|
|
8ba1f26309 | ||
|
|
5a867a83c6 | ||
|
|
8a7e7e4281 | ||
|
|
0e8a6dd961 | ||
|
|
c4a0ec4140 | ||
|
|
07d907fc43 | ||
|
|
b1f83baf04 | ||
|
|
1ff3404e56 | ||
|
|
41190204b3 | ||
|
|
5bc978ee44 | ||
|
|
3b4028f443 | ||
|
|
acc3ee9205 | ||
|
|
d8770ed590 | ||
|
|
be7873a688 | ||
|
|
cf81f1c9f9 | ||
|
|
cde05df1bc | ||
|
|
5fee459945 | ||
|
|
6d67291928 | ||
|
|
4171e23153 | ||
|
|
725f625aba | ||
|
|
a6ea1eb5a9 | ||
|
|
14b983cd0c | ||
|
|
23ddb47e0c | ||
|
|
407aef3786 | ||
|
|
50c7bf77de | ||
|
|
9a7d5a1a51 | ||
|
|
031bd23849 | ||
|
|
7e2dc91ce7 | ||
|
|
c7a81655c4 | ||
|
|
9aa50104db | ||
|
|
3320ee8e05 | ||
|
|
ec380e25ec | ||
|
|
e1b29d0204 | ||
|
|
13313f64c5 | ||
|
|
9cb64e9cf5 | ||
|
|
fd0a1bef65 | ||
|
|
ae9ae098cc | ||
|
|
18f49d0d52 | ||
|
|
a57b6a4706 | ||
|
|
f8aa84a91b | ||
|
|
0154fe6a5a | ||
|
|
6f96818438 | ||
|
|
6433767a98 | ||
|
|
f8037b7ff5 | ||
|
|
7dddd4a5a2 | ||
|
|
85ac37d019 | ||
|
|
499b2bd938 | ||
|
|
5302fb4ba7 | ||
|
|
b58a6512b3 | ||
|
|
e38ccf5376 | ||
|
|
3c9a0ff2f7 | ||
|
|
b8675fa208 | ||
|
|
ddac8f7802 | ||
|
|
c21930de2b | ||
|
|
f2cc1b68c1 | ||
|
|
fc61676732 | ||
|
|
1772d0a894 | ||
|
|
3de424b129 | ||
|
|
d8c289e0cc | ||
|
|
5a17bb2fda | ||
|
|
c45b06657a | ||
|
|
7666077c63 | ||
|
|
542aa56840 | ||
|
|
d2f990366b | ||
|
|
1480faf32f | ||
|
|
38ff3318eb | ||
|
|
5bd253c0da | ||
|
|
9e94ee15e4 | ||
|
|
a026cd91fe | ||
|
|
a52c45bd08 | ||
|
|
88c91aae25 | ||
|
|
b793544cf8 | ||
|
|
63c3690fa8 | ||
|
|
e2ece2c35b | ||
|
|
86d6e96f7a | ||
|
|
87e1ccc6bf | ||
|
|
aa355e22f9 | ||
|
|
d21f758158 | ||
|
|
f2741116a7 | ||
|
|
ea92bf7718 | ||
|
|
e2bbbd7835 | ||
|
|
b38ae3aad3 | ||
|
|
68d8b35ab8 | ||
|
|
5d25b0ae9b | ||
|
|
8fa4df0076 | ||
|
|
909d5ec139 | ||
|
|
64a0bf67db | ||
|
|
a9047947b0 | ||
|
|
d49adfdef3 | ||
|
|
498b665c8a | ||
|
|
14c3824632 | ||
|
|
eb93af3622 | ||
|
|
b7691afb8d | ||
|
|
61c61c8844 | ||
|
|
120207f1f9 | ||
|
|
ceb6a44238 | ||
|
|
6a1b2f0610 | ||
|
|
95d4d90efd | ||
|
|
6605a1c3c2 | ||
|
|
0c754affd9 | ||
|
|
1e45abe988 | ||
|
|
6852e25372 | ||
|
|
ac8256a96b | ||
|
|
0786016436 | ||
|
|
f67b5db2f9 | ||
|
|
949fbdba14 | ||
|
|
558ffee93b | ||
|
|
69274ed7f9 | ||
|
|
7f7d0a0aa2 | ||
|
|
683d49bc71 | ||
|
|
da77f89910 | ||
|
|
9b9310dc84 | ||
|
|
1f94752e12 | ||
|
|
016958cb41 | ||
|
|
b003a2b1e3 | ||
|
|
eef665c896 | ||
|
|
e7492047c0 | ||
|
|
71db6de14f | ||
|
|
246048d8be | ||
|
|
3a7fb0971b | ||
|
|
b672357f78 | ||
|
|
302d8ec3b2 | ||
|
|
cdbf8d6fc5 | ||
|
|
f5f9d4983f | ||
|
|
2d67984536 | ||
|
|
e042bc3a17 | ||
|
|
297e2a13a2 | ||
|
|
4df8f475ce | ||
|
|
638264d874 | ||
|
|
21785a1a08 | ||
|
|
8e50c20c9d | ||
|
|
c5d2ddc156 | ||
|
|
e9adb18d58 | ||
|
|
146408655b | ||
|
|
3b7d583d8f | ||
|
|
4a704dde0c | ||
|
|
f5f8423364 | ||
|
|
00a5f72857 | ||
|
|
f4ec61340f | ||
|
|
a46d22b31f | ||
|
|
7ad8bbe6e7 | ||
|
|
fcbdd00bce | ||
|
|
491a56db10 | ||
|
|
cbe31981b3 | ||
|
|
dafe9824e3 | ||
|
|
b931066573 | ||
|
|
f45e107207 | ||
|
|
ece3bc4d85 | ||
|
|
4b9ee685ac | ||
|
|
cfc43685a4 | ||
|
|
d05d5f594b | ||
|
|
425036adc9 | ||
|
|
cb174592be | ||
|
|
7a350a5e83 | ||
|
|
afadf3ea3f | ||
|
|
c8ccf053ff | ||
|
|
2f74250634 | ||
|
|
ad7faaab81 | ||
|
|
e39a841f6e | ||
|
|
fc7c6afa81 | ||
|
|
51bc9d1569 | ||
|
|
747bbd7c7b | ||
|
|
521ffbfaaf | ||
|
|
cd33e5b274 | ||
|
|
201ea843a0 | ||
|
|
dee210ceec | ||
|
|
ea0e52c5a2 | ||
|
|
b5578ff5bc | ||
|
|
f9ad3cb029 | ||
|
|
30c24c59ed | ||
|
|
6f05c9d327 | ||
|
|
30429b88b3 | ||
|
|
394be61a65 | ||
|
|
d55f6d7baa | ||
|
|
2dfcab170d | ||
|
|
59b2094ad1 | ||
|
|
8046600a55 | ||
|
|
17a655b52a | ||
|
|
75b8587216 | ||
|
|
7c87ab88bc | ||
|
|
b87470a284 | ||
|
|
948bf10bad | ||
|
|
59c3761455 | ||
|
|
b50f5ecf3e | ||
|
|
be9c492406 | ||
|
|
85e1a356fe | ||
|
|
5da4c1653a | ||
|
|
64edd55add | ||
|
|
bd28ee77d9 | ||
|
|
d10a505aa9 | ||
|
|
77f744eba4 | ||
|
|
f8dde00a33 | ||
|
|
380f260027 |
4
.gitignore
vendored
4
.gitignore
vendored
@@ -1,3 +1,7 @@
|
||||
build
|
||||
*.user
|
||||
version.h
|
||||
*.pyc
|
||||
*.todo
|
||||
packaging/make_ubuntu2.sh
|
||||
test_*.log
|
||||
|
||||
5
AUTHORS
5
AUTHORS
@@ -29,6 +29,9 @@ Contributors:
|
||||
Oskari Rauta : separator plugin, gradients
|
||||
Michael Messmore : Support for Path in .desktop files
|
||||
Matthew Otnel : config option systray_name_filter
|
||||
Ryan Gray, Jeff Blake (https://gitlab.com/berkley4) : battery format
|
||||
aaaz (https://gitlab.com/aaaz) : clock fixes
|
||||
heisenbug (https://gitlab.com/heisenbugh) : taskbar button tinting with icon color
|
||||
|
||||
Translations:
|
||||
Bosnian:
|
||||
@@ -43,3 +46,5 @@ Translations:
|
||||
Daniel Napora <napcok@gmail.com>
|
||||
Serbian:
|
||||
Dino Duratović <dinomol@mail.com>
|
||||
Spanish:
|
||||
Vic <vicmz@yandex.com>
|
||||
|
||||
@@ -6,6 +6,7 @@ option( ENABLE_TINT2CONF "Enable tint2conf build, a GTK+2 theme configurator for
|
||||
option( ENABLE_EXTRA_THEMES "Install additional tint2 themes" ON )
|
||||
option( ENABLE_RSVG "Rsvg support (launcher only)" ON )
|
||||
option( ENABLE_SN "Startup notification support" ON )
|
||||
option( ENABLE_TRACING "Build tint2 with tracing instrumentation" OFF )
|
||||
option( ENABLE_ASAN "Build tint2 with AddressSanitizer" OFF )
|
||||
option( ENABLE_BACKTRACE "Dump a backtrace in case of fatal errors (e.g. X11 I/O error)" OFF )
|
||||
option( ENABLE_BACKTRACE_ON_SIGNAL "Dump a backtrace also when receiving signals such as SIGSEGV" OFF )
|
||||
@@ -24,7 +25,7 @@ endif()
|
||||
include( FindPkgConfig )
|
||||
include( CheckLibraryExists )
|
||||
include( CheckCSourceCompiles )
|
||||
pkg_check_modules( X11 REQUIRED x11 xcomposite xdamage xinerama xrender xrandr>=1.3 )
|
||||
pkg_check_modules( X11 REQUIRED x11 xcomposite xdamage xinerama xext xrender xrandr>=1.3 )
|
||||
pkg_check_modules( PANGOCAIRO REQUIRED pangocairo )
|
||||
pkg_check_modules( PANGO REQUIRED pango )
|
||||
pkg_check_modules( CAIRO REQUIRED cairo )
|
||||
@@ -61,6 +62,17 @@ else()
|
||||
set(BACKTRACE_L_FLAGS "")
|
||||
endif()
|
||||
|
||||
check_c_source_compiles(
|
||||
"#define print(x) _Generic((x), default : print_unknown)(x) \n void print_unknown(){} \n int main () { print(0); }"
|
||||
HAS_GENERIC)
|
||||
|
||||
if(HAS_GENERIC)
|
||||
add_definitions(-DHAS_GENERIC)
|
||||
set(CSTD "c11")
|
||||
else()
|
||||
set(CSTD "c99")
|
||||
endif(HAS_GENERIC)
|
||||
|
||||
if( ENABLE_RSVG )
|
||||
pkg_check_modules( RSVG librsvg-2.0>=2.14.0 )
|
||||
endif( ENABLE_RSVG )
|
||||
@@ -83,10 +95,10 @@ if( NOT IMLIB_BUILD_WITH_X )
|
||||
endif( NOT IMLIB_BUILD_WITH_X )
|
||||
|
||||
|
||||
add_definitions( -D_BSD_SOURCE -D_DEFAULT_SOURCE -D_WITH_GETLINE )
|
||||
|
||||
if(CMAKE_SYSTEM_NAME STREQUAL "Linux")
|
||||
add_definitions( -D_POSIX_C_SOURCE=200809L -D_BSD_SOURCE -D_DEFAULT_SOURCE )
|
||||
else(CMAKE_SYSTEM_NAME STREQUAL "Linux")
|
||||
add_definitions( -D_WITH_GETLINE )
|
||||
add_definitions( -D_POSIX_C_SOURCE=200809L )
|
||||
endif(CMAKE_SYSTEM_NAME STREQUAL "Linux")
|
||||
|
||||
|
||||
@@ -115,8 +127,14 @@ include_directories( ${PROJECT_BINARY_DIR}
|
||||
|
||||
set( SOURCES src/config.c
|
||||
src/panel.c
|
||||
src/server.c
|
||||
src/tint.c
|
||||
src/util/server.c
|
||||
src/main.c
|
||||
src/init.c
|
||||
src/util/signals.c
|
||||
src/util/tracing.c
|
||||
src/mouse_actions.c
|
||||
src/drag_and_drop.c
|
||||
src/default_icon.c
|
||||
src/clock/clock.c
|
||||
src/systray/systraybar.c
|
||||
src/launcher/launcher.c
|
||||
@@ -135,11 +153,16 @@ set( SOURCES src/config.c
|
||||
src/tint2rc.c
|
||||
src/util/area.c
|
||||
src/util/common.c
|
||||
src/util/fps_distribution.c
|
||||
src/util/strnatcmp.c
|
||||
src/util/timer.c
|
||||
src/util/cache.c
|
||||
src/util/color.c
|
||||
src/util/strlcat.c
|
||||
src/util/print.c
|
||||
src/util/gradient.c
|
||||
src/util/test.c
|
||||
src/util/uevent.c
|
||||
src/util/window.c )
|
||||
|
||||
if( ENABLE_BATTERY )
|
||||
@@ -180,7 +203,6 @@ endif( ENABLE_SN)
|
||||
|
||||
if( ENABLE_UEVENT )
|
||||
add_definitions( -DENABLE_UEVENT )
|
||||
set( SOURCES ${SOURCES} src/util/uevent.c)
|
||||
endif( ENABLE_UEVENT )
|
||||
|
||||
if(ENABLE_BACKTRACE)
|
||||
@@ -210,14 +232,23 @@ if( ENABLE_TINT2CONF )
|
||||
endif( ENABLE_TINT2CONF )
|
||||
|
||||
if( ENABLE_ASAN )
|
||||
SET(ASAN_C_FLAGS " -O0 -g3 -gdwarf-2 -fsanitize=address -fno-common -fno-omit-frame-pointer -rdynamic -Wshadow")
|
||||
SET(ASAN_L_FLAGS " -O0 -g3 -gdwarf-2 -fsanitize=address -fno-common -fno-omit-frame-pointer -rdynamic -fuse-ld=gold ")
|
||||
SET(ASAN_C_FLAGS " -O0 -g3 -fsanitize=address -fno-common -fno-omit-frame-pointer -rdynamic -Wshadow")
|
||||
SET(ASAN_L_FLAGS " -O0 -g3 -fsanitize=address -fno-common -fno-omit-frame-pointer -rdynamic -fuse-ld=gold ")
|
||||
else()
|
||||
SET(ASAN_C_FLAGS "")
|
||||
SET(ASAN_L_FLAGS "")
|
||||
endif()
|
||||
|
||||
add_custom_target( version ALL "${PROJECT_SOURCE_DIR}/get_version.sh" "\"${PROJECT_SOURCE_DIR}\"" )
|
||||
if( ENABLE_TRACING )
|
||||
add_definitions( -DHAVE_TRACING )
|
||||
SET(TRACING_C_FLAGS " -finstrument-functions -finstrument-functions-exclude-file-list=tracing.c -finstrument-functions-exclude-function-list=get_time,gettime -O0 -g3 -fno-common -fno-omit-frame-pointer -rdynamic")
|
||||
SET(TRACING_L_FLAGS " -O0 -g3 -fno-common -fno-omit-frame-pointer -rdynamic")
|
||||
else()
|
||||
SET(TRACING_C_FLAGS "")
|
||||
SET(TRACING_L_FLAGS "")
|
||||
endif()
|
||||
|
||||
add_custom_target( version ALL "${PROJECT_SOURCE_DIR}/get_version.sh" )
|
||||
|
||||
link_directories( ${X11_LIBRARY_DIRS}
|
||||
${PANGOCAIRO_LIBRARY_DIRS}
|
||||
@@ -251,13 +282,13 @@ endif( RT_LIBRARY )
|
||||
target_link_libraries( tint2 m )
|
||||
|
||||
add_dependencies( tint2 version )
|
||||
set_target_properties( tint2 PROPERTIES COMPILE_FLAGS "-Wall -Wpointer-arith -fno-strict-aliasing -pthread -std=c99 ${ASAN_C_FLAGS}" )
|
||||
set_target_properties( tint2 PROPERTIES LINK_FLAGS "-pthread -fno-strict-aliasing ${ASAN_L_FLAGS} ${BACKTRACE_L_FLAGS}" )
|
||||
set_target_properties( tint2 PROPERTIES COMPILE_FLAGS "-Wall -Wpointer-arith -fno-strict-aliasing -pthread -std=${CSTD} ${ASAN_C_FLAGS} ${TRACING_C_FLAGS}" )
|
||||
set_target_properties( tint2 PROPERTIES LINK_FLAGS "-pthread -fno-strict-aliasing ${ASAN_L_FLAGS} ${BACKTRACE_L_FLAGS} ${TRACING_L_FLAGS}" )
|
||||
|
||||
install( TARGETS tint2 DESTINATION bin )
|
||||
install( FILES tint2.svg DESTINATION ${CMAKE_INSTALL_DATADIR}/icons/hicolor/scalable/apps )
|
||||
install( FILES tint2.desktop DESTINATION ${CMAKE_INSTALL_DATADIR}/applications )
|
||||
install( FILES themes/tint2rc DESTINATION /etc/xdg/tint2 )
|
||||
install( FILES themes/tint2rc DESTINATION ${CMAKE_INSTALL_FULL_SYSCONFDIR}/xdg/tint2 )
|
||||
install( FILES default_icon.png DESTINATION ${CMAKE_INSTALL_DATADIR}/tint2 )
|
||||
install( FILES AUTHORS ChangeLog README.md doc/tint2.md DESTINATION ${docdir} )
|
||||
install( FILES doc/manual.html doc/readme.html DESTINATION ${htmldir} )
|
||||
|
||||
96
ChangeLog
96
ChangeLog
@@ -1,3 +1,93 @@
|
||||
2017-12-30 16.1
|
||||
- Fixes:
|
||||
- Fixed several use-after-free errors in the timer code
|
||||
- Merged patches and fixed other warnings on OpenBSD
|
||||
- Task, Button, Executor: add a bit of slack in the pango text layout,
|
||||
to avoid wrapping due to rounding errors
|
||||
|
||||
2017-12-20 16.0
|
||||
- Fixes:
|
||||
- Taskbar: `taskbar_distribute_size = 1` now playes well with `task_align = center` and
|
||||
`task_align = right` (issue #688)
|
||||
- Enhancements:
|
||||
- Added Spanish translation (contributed by Vicmz)
|
||||
- Executor: updated tooltip documentation (issue #676)
|
||||
- Systray: warn on duplicate config option systray_name_filter (issue #652)
|
||||
- Taskbar: thumbnail support in tooltips
|
||||
- Use C11 if possible to support generic printing for unit tests (should fall back to C99)
|
||||
|
||||
2017-11-05 15.3
|
||||
- Fixes:
|
||||
- Launcher: Reset signal mask before executing commands (issue #674)
|
||||
- cmake: Do not hardcode path to /etc
|
||||
|
||||
2017-10-01 15.2
|
||||
- Fixes:
|
||||
- Battery info is now again displayed even when current sensor is missing (https://github.com/jmc-88/tint3/issues/34)
|
||||
- Text elements compute their size correctly (issue #671)
|
||||
- Window order persists on panel restart (issue #615)
|
||||
|
||||
2017-09-08 15.1
|
||||
- Fixes:
|
||||
- Fixed build on non-Linux and non-x86 systems
|
||||
|
||||
2017-09-02 15.0
|
||||
- Fixes:
|
||||
- Clock, executors and other timers fire correctly after waking up from suspend
|
||||
- One-shot timers are restarted correctly from their own callbacks
|
||||
- Clock is refreshed with better accuracy (thanks @aaaz)
|
||||
- Panel: by popular demand, the old struts behavior with autohide has been restored (issue #619);
|
||||
if you encounter applications that interact poorly with it,
|
||||
you might have better luck with strut_policy = minimum,
|
||||
if that does not work, you will have to turn off autohide.
|
||||
- Enhancements:
|
||||
- Panel:
|
||||
- _NET_WM_PID is set correctly, so now tint2 can be interacted with more easily from wmctrl and similar apps
|
||||
- Taskbar: new config option taskbar_hide_different_desktop
|
||||
- Battery:
|
||||
- New config option bat1_format and bat2_format
|
||||
- New config option battery_full_cmd
|
||||
- Better "Unknown" state handling
|
||||
- Executor:
|
||||
- Hide if output is empty
|
||||
- If no user tooltip is set, displays the script standard error as tooltip.
|
||||
Tooltip is multiline, can be cleared with the VT100 clear screen sequence, in shell: (>&2 echo -en "\033[2J").
|
||||
Long tooltips are truncated to 4096 characters.
|
||||
- Launcher:
|
||||
- Drag and drop now handles correctly text/uri-list
|
||||
- Support for Terminal=true
|
||||
- Support for %f and %F
|
||||
- Configuration changes:
|
||||
- Removed primary_monitor_first as it was conflicting with taskbar behavior; use *_monitor = primary instead.
|
||||
- Other:
|
||||
- Major code refactoring
|
||||
- Dropping "0." from the version number and no longer using semver ("Breaking.Feature.Fix").
|
||||
- Tint2 will always strive to be backwards compatible with respect to the configuration format.
|
||||
- Very few configurations changes have been broken between 2010 (0.10) - 2017 (0.14);
|
||||
in all cases they were minor options that caused incorrect behavior,
|
||||
and the changes were described better by "Feature" or "Fix".
|
||||
- Practically all releases starting from 0.10 have been very stable,
|
||||
so there is no point in staying in "0." anymore.
|
||||
- But I don't want the project to get stuck in "1." forever.
|
||||
- The new versioning scheme is the following:
|
||||
- Version numbers will have the format "Feature.Fix", where:
|
||||
- "Feature" is increased when significant new features are added.
|
||||
- "Fix" is increased for bugfixes or minor changes.
|
||||
- 0.14.6 will be followed by 15.0.
|
||||
|
||||
2017-06-11 0.14.6
|
||||
- Fixes:
|
||||
- Take into account border width when computing text height
|
||||
- Taskbar: Fix task icon size limits
|
||||
- Executor: Do not output last line if it is not terminated by newline
|
||||
- Enhancements:
|
||||
- Re-execute tint2 on SIGUSR2.
|
||||
This is useful for preserving config options and environment when updating tint2.
|
||||
|
||||
2017-05-21 0.14.5
|
||||
- Fixes:
|
||||
- Fixed a couple of memory leaks
|
||||
|
||||
2017-04-29 0.14.4
|
||||
- Fixes:
|
||||
- Fix regression in executor (issue #639)
|
||||
@@ -889,3 +979,9 @@ released tint-0.2
|
||||
2008-04-22
|
||||
- fork ttm projet from p://code.google.com/p/ttm/ (by Pål Staurland staura@gmail.com)
|
||||
while the projet is no longer in developpement, have not changed the name of 'tint'.
|
||||
.
|
||||
.
|
||||
.
|
||||
.
|
||||
.
|
||||
.
|
||||
|
||||
19
README.md
19
README.md
@@ -1,5 +1,5 @@
|
||||
# Latest stable release: 0.14.4
|
||||
Changes: https://gitlab.com/o9000/tint2/blob/0.14.4/ChangeLog
|
||||
# Latest stable release: 16.1
|
||||
Changes: https://gitlab.com/o9000/tint2/blob/16.1/ChangeLog
|
||||
|
||||
Documentation: [doc/tint2.md](doc/tint2.md)
|
||||
|
||||
@@ -8,7 +8,7 @@ Compile it with (after you install the [dependencies](https://gitlab.com/o9000/t
|
||||
```
|
||||
git clone https://gitlab.com/o9000/tint2.git
|
||||
cd tint2
|
||||
git checkout 0.14.4
|
||||
git checkout 16.1
|
||||
mkdir build
|
||||
cd build
|
||||
cmake ..
|
||||
@@ -61,9 +61,9 @@ tint2 is a simple panel/taskbar made for modern X window managers. It was specif
|
||||
|
||||
# Known issues
|
||||
|
||||
* Graphic glitches on Intel graphics cards can be avoided by changing the acceleration method to UXA ([issue 595](https://gitlab.com/o9000/tint2/issues/595))
|
||||
* Window managers that do not follow exactly the EWMH specification might not interact well with tint2 (known issues for [awesome](https://gitlab.com/o9000/tint2/issues/385), [bspwm](https://gitlab.com/o9000/tint2/issues/524). [openbox-multihead](https://gitlab.com/o9000/tint2/issues/456))
|
||||
* Full transparency requires a compositor such as Compton (if not provided already by the window manager, as in Compiz/Unity, KDE or XFCE)
|
||||
* Graphical glitches on Intel graphics cards can be avoided by changing the acceleration method to UXA ([issue 595](https://gitlab.com/o9000/tint2/issues/595))
|
||||
* Window managers that do not follow exactly the EWMH specification might not interact well with tint2 ([issue 627](https://gitlab.com/o9000/tint2/issues/627)).
|
||||
* Full transparency requires a compositor such as Compton (if not provided already by the window manager, as in Compiz/Unity, KDE or XFCE).
|
||||
|
||||
# How can I help out?
|
||||
|
||||
@@ -85,7 +85,7 @@ tint2 is a simple panel/taskbar made for modern X window managers. It was specif
|
||||
|
||||
## Various configs:
|
||||
|
||||

|
||||
* [Screenshots](https://gitlab.com/o9000/tint2/wikis/screenshots)
|
||||
|
||||
## Demos
|
||||
|
||||
@@ -93,3 +93,8 @@ tint2 is a simple panel/taskbar made for modern X window managers. It was specif
|
||||
* [Executor](https://gitlab.com/o9000/tint2/wikis/whats-new-0.12.4.gif)
|
||||
* [Mouse over effects](https://gitlab.com/o9000/tint2/wikis/whats-new-0.12.3.gif)
|
||||
* [Distribute size between taskbars, freespace](https://gitlab.com/o9000/tint2/wikis/whats-new-0.12.gif)
|
||||
|
||||
## More
|
||||
|
||||
* [Tint2 wiki](https://gitlab.com/o9000/tint2/wikis/Home)
|
||||
Home)
|
||||
|
||||
@@ -270,6 +270,8 @@ Try to respect as much as possible the order of the options as given below.</p><
|
||||
<li><code>color</code> is specified in hex RGB, e.g. #ff0000 is red</li>
|
||||
<li><code>opacity</code> varies from (0 to 100), where 0 is fully transparent, 100 is fully opaque</li>
|
||||
</ul></li>
|
||||
<li><p><code>border_content_tint_weight = integer</code> : Mixes the border color with the content color (for tasks, this is the average color of the window icon). Values must be between 0 (no mixing) and 100 (fully replaces the color). <em>(since 16.0)</em></p></li>
|
||||
<li><p><code>background_content_tint_weight = integer</code> : Mixes the background color with the content color (for tasks, this is the average color of the window icon). Values must be between 0 (no mixing) and 100 (fully replaces the color). <em>(since 16.0)</em></p></li>
|
||||
</ul>
|
||||
<p>You can define as many backgrounds as you want. For example, the following config defines two backgrounds:</p><pre class="highlight plaintext"><code>rounded = 1
|
||||
border_width = 0
|
||||
@@ -364,12 +366,12 @@ gradient_id_pressed = 2
|
||||
<li><code>:</code> adds a separator. You can specify more than one. <em>(since 0.13.0)</em></li>
|
||||
</ul>
|
||||
<p>For example, <code>panel_items = STC</code> will show the systray, the taskbar and the clock (from left to right).</p></li>
|
||||
<li><p><code>panel_monitor = monitor (all or 1 or 2 or ...)</code> : Which monitor tint2 draws the panel on</p>
|
||||
<li><p><code>panel_monitor = monitor (all or primary or 1 or 2 or ...)</code> : Which monitor tint2 draws the panel on</p>
|
||||
<ul>
|
||||
<li>The first monitor is <code>1</code></li>
|
||||
<li>Use <code>panel_monitor = all</code> to get a separate panel per monitor</li>
|
||||
</ul></li>
|
||||
<li><p><code>primary_monitor_first = boolean (0 or 1)</code> : Place the primary monitor before all the other monitors in the list. <em>(since 0.12.4)</em></p></li>
|
||||
<li><p><code>primary_monitor_first = boolean (0 or 1)</code> : Place the primary monitor before all the other monitors in the list. <em>(since 0.12.4; removed in 1.0, use <code>primary</code> instead)</em></p></li>
|
||||
</ul>
|
||||
<p><img src="images/panel_padding.jpg" alt=""></p>
|
||||
<ul>
|
||||
@@ -456,6 +458,7 @@ panel_size = 94% 30
|
||||
<li><p><code>taskbar_active_background_id = integer</code> : Which background to use for the taskbar of the current virtual desktop.</p></li>
|
||||
<li><p><code>taskbar_hide_inactive_tasks = boolean (0 or 1)</code> : If enabled, the taskbar shows only the active task. <em>(since 0.12)</em></p></li>
|
||||
<li><p><code>taskbar_hide_different_monitor = boolean (0 or 1)</code> : If enabled, the taskbar shows only the tasks from the current monitor. Useful when running different tint2 instances on different monitors, each one having its own config. <em>(since 0.12)</em></p></li>
|
||||
<li><p><code>taskbar_hide_different_desktop = boolean (0 or 1)</code> : If enabled, the taskbar shows only the tasks from the current desktop. Useful to make multi-desktop taskbars more compact, but still allow desktop switching with mouse click. <em>(since 1.0)</em></p></li>
|
||||
<li><p><code>taskbar_always_show_all_desktop_tasks = boolean (0 or 1)</code> : Has effect only if <code>taskbar_mode = multi_desktop</code>. If enabled, tasks that appear on all desktops are shown on all taskbars. Otherwise, they are shown only on the taskbar of the current desktop. <em>(since 0.12.4)</em></p></li>
|
||||
<li><p><code>taskbar_sort_order = none/title/center</code> : Specifies the sort order of the tasks on the taskbar. <em>(since 0.12)</em></p>
|
||||
<ul>
|
||||
@@ -480,6 +483,8 @@ panel_size = 94% 30
|
||||
<li><p><code>task_text = boolean (0 or 1)</code> : Whether to display the task text.</p></li>
|
||||
<li><p><code>task_centered = boolean (0 or 1)</code> : Whether the task text is centered.</p></li>
|
||||
<li><p><code>task_tooltip = boolean (0 or 1)</code> : Whether to show tooltips for tasks.</p></li>
|
||||
<li><p><code>task_thumbnail = boolean (0 or 1)</code> : Whether to show thumbnail tooltips for tasks. <em>(since 16.0)</em></p></li>
|
||||
<li><p><code>task_thumbnail_size = width</code> : Thumbnail size. <em>(since 16.0)</em></p></li>
|
||||
<li><p><code>task_maximum_size = width height</code></p>
|
||||
<ul>
|
||||
<li><code>width</code> is used with horizontal panels to limit the size of the tasks. Use <code>width = 0</code> to get full taskbar width.</li>
|
||||
@@ -523,7 +528,7 @@ panel_size = 94% 30
|
||||
<li><p><code>systray_sort = ascending/descending/left2right/right2left</code> : Specifies the sorting order for the icons in the systray: in ascending/descending alphabetical order of the icon title, or always add icons to the right/left (note that with <code>left2right</code> or <code>right2left</code> the order can be different on panel restart).</p></li>
|
||||
<li><p><code>systray_icon_size = max_icon_size</code> : Set the maximum system tray icon size to <code>number</code>. Set to <code>0</code> for automatic icon sizing.</p></li>
|
||||
<li><p><code>systray_icon_asb = alpha (0 to 100) saturation (-100 to 100) brightness (-100 to 100)</code> : Adjust the systray icons color and transparency.</p></li>
|
||||
<li><p><code>systray_monitor = integer (1, 2, ...)</code> : On which monitor to draw the systray. The first monitor is <code>1</code>. <em>(since 0.12)</em></p></li>
|
||||
<li><p><code>systray_monitor = integer (1, 2, ...) or primary</code> : On which monitor to draw the systray. The first monitor is <code>1</code>. <em>(since 0.12)</em></p></li>
|
||||
<li><p><code>systray_name_filter = string</code> : Regular expression to identify icon names to be hidden. For example, <code>^audacious$</code> will hide icons with the exact name <code>audacious</code>, while <code>aud</code> will hide any icons having <code>aud</code> in the name. <em>(since 0.13.1)</em></p></li>
|
||||
</ul>
|
||||
<h3 id="clock">Clock<a name="clock" href="#clock" class="md2man-permalink" title="permalink"></a></h3>
|
||||
@@ -566,9 +571,19 @@ panel_size = 94% 30
|
||||
<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 "battery low"</code> : Command to execute when the battery is low.</p></li>
|
||||
<li><p><code>battery_full_cmd = notify-send "battery full"</code> : Command to execute when the battery is full.</p></li>
|
||||
<li><p><code>bat1_font = [FAMILY-LIST] [STYLE-OPTIONS] [SIZE]</code></p></li>
|
||||
<li><p><code>bat2_font = [FAMILY-LIST] [STYLE-OPTIONS] [SIZE]</code></p></li>
|
||||
<li><p><code>battery_font_color = color opacity (0 to 100)</code></p></li>
|
||||
<li><p><code>bat1_format = FORMAT_STRING</code> : Format for battery line 1. Default: %p. <em>(since 1.0)</em> Format specification:</p>
|
||||
<ul>
|
||||
<li>%s: State (charging, discharging, full, unknown).</li>
|
||||
<li>%m: Minutes left until completely charged/discharged (estimated).</li>
|
||||
<li>%h: Hours left until completely charged/discharged (estimated).</li>
|
||||
<li>%t: Time left. Shows "hrs:mins" when charging/discharging, or "Ful\" when full.</li>
|
||||
<li>%p: Percentage. Includes the % sign.</li>
|
||||
</ul></li>
|
||||
<li><p><code>bat2_format = FORMAT_STRING</code> : Format for battery line 2. Default: %t. <em>(since 1.0)</em></p></li>
|
||||
<li><p><code>battery_padding = horizontal_padding vertical_padding</code></p></li>
|
||||
<li><p><code>battery_background_id = integer</code> : Which background to use for the battery.</p></li>
|
||||
<li><p><code>battery_tooltip_enabled = boolean (0 or 1)</code> : Enable/disable battery tooltips. <em>(since 0.12.3)</em></p></li>
|
||||
@@ -590,7 +605,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 '\e[2J'</code>, C: <code>printf("\x1b[2J");</code>). If the standard error is empty, the tooltip will show information about the time when the command was last executed. <em>(since 0.12.4)</em></p></li>
|
||||
<li><p><code>execp_font = [FAMILY-LIST] [STYLE-OPTIONS] [SIZE]</code> : The font used to draw the text. <em>(since 0.12.4)</em></p></li>
|
||||
<li><p><code>execp_font_color = color opacity</code> : The font color. <em>(since 0.12.4)</em></p></li>
|
||||
<li><p><code>execp_markup = boolean (0 or 1)</code> : If non-zero, the output of the command is treated as Pango markup, which allows rich text formatting. The format is <a href="https://developer.gnome.org/pygtk/stable/pango-markup-language.html">documented here</a>. Note that using this with commands that print data downloaded from the Internet is a possible security risk. <em>(since 0.12.4)</em></p></li>
|
||||
@@ -683,6 +698,7 @@ execp_interval = 1
|
||||
It is based on ttm, originally written by Pål Staurland <a href="mailto:staura@gmail.com">staura@gmail.com</a>.</p><p>This manual page was originally written by Daniel Moerner <a href="mailto:dmoerner@gmail.com">dmoerner@gmail.com</a>, for the Debian project (but may be used by others).
|
||||
It was adopted from the tint2 docs.</p><h2 id="see-also">SEE ALSO<a name="see-also" href="#see-also" class="md2man-permalink" title="permalink"></a></h2><p>The main website <a href="https://gitlab.com/o9000/tint2">https://gitlab.com/o9000/tint2</a>
|
||||
and the wiki page at <a href="https://gitlab.com/o9000/tint2/wikis/home">https://gitlab.com/o9000/tint2/wikis/home</a>.</p><p>This documentation is also provided in HTML and Markdown format in the system's default location
|
||||
for documentation files, usually <code>/usr/share/doc/tint2</code> or <code>/usr/local/share/doc/tint2</code>.</p>
|
||||
for documentation files, usually <code>/usr/share/doc/tint2</code> or <code>/usr/local/share/doc/tint2</code>.
|
||||
.</p>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -199,9 +199,9 @@ pre {
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<h1 id="latest-stable-release-0-14-4"><span class="md2man-title">Latest</span> <span class="md2man-section">stable</span> <span class="md2man-date">release:</span> <span class="md2man-source">0.14.4</span><a name="latest-stable-release-0-14-4" href="#latest-stable-release-0-14-4" class="md2man-permalink" title="permalink"></a></h1><p>Changes: <a href="https://gitlab.com/o9000/tint2/blob/0.14.4/ChangeLog">https://gitlab.com/o9000/tint2/blob/0.14.4/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-1"><span class="md2man-title">Latest</span> <span class="md2man-section">stable</span> <span class="md2man-date">release:</span> <span class="md2man-source">16.1</span><a name="latest-stable-release-16-1" href="#latest-stable-release-16-1" class="md2man-permalink" title="permalink"></a></h1><p>Changes: <a href="https://gitlab.com/o9000/tint2/blob/16.1/ChangeLog">https://gitlab.com/o9000/tint2/blob/16.1/ChangeLog</a></p><p>Documentation: <a href="manual.html">manual.html</a></p><p>Compile it with (after you install the <a href="https://gitlab.com/o9000/tint2/wikis/Install#dependencies">dependencies</a>):</p><pre class="highlight plaintext"><code>git clone https://gitlab.com/o9000/tint2.git
|
||||
cd tint2
|
||||
git checkout 0.14.4
|
||||
git checkout 16.1
|
||||
mkdir build
|
||||
cd build
|
||||
cmake ..
|
||||
@@ -239,9 +239,9 @@ update-mime-database /usr/local/share/mime
|
||||
</ul>
|
||||
<h1 id="known-issues">Known issues<a name="known-issues" href="#known-issues" class="md2man-permalink" title="permalink"></a></h1>
|
||||
<ul>
|
||||
<li>Graphic glitches on Intel graphics cards can be avoided by changing the acceleration method to UXA (<a href="https://gitlab.com/o9000/tint2/issues/595">issue 595</a>)</li>
|
||||
<li>Window managers that do not follow exactly the EWMH specification might not interact well with tint2 (known issues for <a href="https://gitlab.com/o9000/tint2/issues/385">awesome</a>, <a href="https://gitlab.com/o9000/tint2/issues/524">bspwm</a>. <a href="https://gitlab.com/o9000/tint2/issues/456">openbox-multihead</a>)</li>
|
||||
<li>Full transparency requires a compositor such as Compton (if not provided already by the window manager, as in Compiz/Unity, KDE or XFCE)</li>
|
||||
<li>Graphical glitches on Intel graphics cards can be avoided by changing the acceleration method to UXA (<a href="https://gitlab.com/o9000/tint2/issues/595">issue 595</a>)</li>
|
||||
<li>Window managers that do not follow exactly the EWMH specification might not interact well with tint2 (<a href="https://gitlab.com/o9000/tint2/issues/627">issue 627</a>).</li>
|
||||
<li>Full transparency requires a compositor such as Compton (if not provided already by the window manager, as in Compiz/Unity, KDE or XFCE).</li>
|
||||
</ul>
|
||||
<h1 id="how-can-i-help-out">How can I help out?<a name="how-can-i-help-out" href="#how-can-i-help-out" class="md2man-permalink" title="permalink"></a></h1>
|
||||
<ul>
|
||||
@@ -256,12 +256,21 @@ update-mime-database /usr/local/share/mime
|
||||
<li>Downloads: <a href="https://gitlab.com/o9000/tint2-archive/tree/master">https://gitlab.com/o9000/tint2-archive/tree/master</a> or <a href="https://code.google.com/p/tint2/downloads/list">https://code.google.com/p/tint2/downloads/list</a></li>
|
||||
<li>Old project location (inactive): <a href="https://code.google.com/p/tint2">https://code.google.com/p/tint2</a></li>
|
||||
</ul>
|
||||
<h1 id="screenshots">Screenshots<a name="screenshots" href="#screenshots" class="md2man-permalink" title="permalink"></a></h1><h2 id="default-config">Default config:<a name="default-config" href="#default-config" class="md2man-permalink" title="permalink"></a></h2><p><img src="https://gitlab.com/o9000/tint2/uploads/948fa74eca60864352a033580350b4c3/Screenshot_2016-01-23_14-42-57.png" alt="Screenshot_2016-01-23_14-42-57"></p><h2 id="various-configs">Various configs:<a name="various-configs" href="#various-configs" class="md2man-permalink" title="permalink"></a></h2><p><img src="https://gitlab.com/o9000/tint2/wikis/screenshot.png" alt="screenshot"></p><h2 id="demos">Demos<a name="demos" href="#demos" class="md2man-permalink" title="permalink"></a></h2>
|
||||
<h1 id="screenshots">Screenshots<a name="screenshots" href="#screenshots" class="md2man-permalink" title="permalink"></a></h1><h2 id="default-config">Default config:<a name="default-config" href="#default-config" class="md2man-permalink" title="permalink"></a></h2><p><img src="https://gitlab.com/o9000/tint2/uploads/948fa74eca60864352a033580350b4c3/Screenshot_2016-01-23_14-42-57.png" alt="Screenshot_2016-01-23_14-42-57"></p><h2 id="various-configs">Various configs:<a name="various-configs" href="#various-configs" class="md2man-permalink" title="permalink"></a></h2>
|
||||
<ul>
|
||||
<li><a href="https://gitlab.com/o9000/tint2/wikis/screenshots">Screenshots</a></li>
|
||||
</ul>
|
||||
<h2 id="demos">Demos<a name="demos" href="#demos" class="md2man-permalink" title="permalink"></a></h2>
|
||||
<ul>
|
||||
<li><a href="https://gitlab.com/o9000/tint2/wikis/whats-new-0.13.0.gif">Compact panel, separator, color gradients</a></li>
|
||||
<li><a href="https://gitlab.com/o9000/tint2/wikis/whats-new-0.12.4.gif">Executor</a></li>
|
||||
<li><a href="https://gitlab.com/o9000/tint2/wikis/whats-new-0.12.3.gif">Mouse over effects</a></li>
|
||||
<li><a href="https://gitlab.com/o9000/tint2/wikis/whats-new-0.12.gif">Distribute size between taskbars, freespace</a></li>
|
||||
</ul>
|
||||
<h2 id="more">More<a name="more" href="#more" class="md2man-permalink" title="permalink"></a></h2>
|
||||
<ul>
|
||||
<li><a href="https://gitlab.com/o9000/tint2/wikis/Home">Tint2 wiki</a>
|
||||
Home)</li>
|
||||
</ul>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
39
doc/tint2.1
39
doc/tint2.1
@@ -1,4 +1,4 @@
|
||||
.TH TINT2 1 "2017\-04\-29" 0.14.4
|
||||
.TH TINT2 1 "2017\-12\-30" 16.1
|
||||
.SH NAME
|
||||
.PP
|
||||
tint2 \- lightweight panel/taskbar
|
||||
@@ -150,6 +150,10 @@ The tint2 config file starts with the options defining background elements with
|
||||
.IP \(bu 2
|
||||
\fB\fCopacity\fR varies from (0 to 100), where 0 is fully transparent, 100 is fully opaque
|
||||
.RE
|
||||
.IP \(bu 2
|
||||
\fB\fCborder_content_tint_weight = integer\fR : Mixes the border color with the content color (for tasks, this is the average color of the window icon). Values must be between 0 (no mixing) and 100 (fully replaces the color). \fI(since 16.0)\fP
|
||||
.IP \(bu 2
|
||||
\fB\fCbackground_content_tint_weight = integer\fR : Mixes the background color with the content color (for tasks, this is the average color of the window icon). Values must be between 0 (no mixing) and 100 (fully replaces the color). \fI(since 16.0)\fP
|
||||
.RE
|
||||
.PP
|
||||
You can define as many backgrounds as you want. For example, the following config defines two backgrounds:
|
||||
@@ -309,7 +313,7 @@ gradient_id_pressed = 2
|
||||
.PP
|
||||
For example, \fB\fCpanel_items = STC\fR will show the systray, the taskbar and the clock (from left to right).
|
||||
.IP \(bu 2
|
||||
\fB\fCpanel_monitor = monitor (all or 1 or 2 or ...)\fR : Which monitor tint2 draws the panel on
|
||||
\fB\fCpanel_monitor = monitor (all or primary or 1 or 2 or ...)\fR : Which monitor tint2 draws the panel on
|
||||
.RS
|
||||
.IP \(bu 2
|
||||
The first monitor is \fB\fC1\fR
|
||||
@@ -317,7 +321,7 @@ The first monitor is \fB\fC1\fR
|
||||
Use \fB\fCpanel_monitor = all\fR to get a separate panel per monitor
|
||||
.RE
|
||||
.IP \(bu 2
|
||||
\fB\fCprimary_monitor_first = boolean (0 or 1)\fR : Place the primary monitor before all the other monitors in the list. \fI(since 0.12.4)\fP
|
||||
\fB\fCprimary_monitor_first = boolean (0 or 1)\fR : Place the primary monitor before all the other monitors in the list. \fI(since 0.12.4; removed in 1.0, use \fB\fCprimary\fR instead)\fP
|
||||
.RE
|
||||
.PP
|
||||
[](images/panel_padding.jpg)
|
||||
@@ -461,6 +465,8 @@ You can switch between virtual desktops.
|
||||
.IP \(bu 2
|
||||
\fB\fCtaskbar_hide_different_monitor = boolean (0 or 1)\fR : If enabled, the taskbar shows only the tasks from the current monitor. Useful when running different tint2 instances on different monitors, each one having its own config. \fI(since 0.12)\fP
|
||||
.IP \(bu 2
|
||||
\fB\fCtaskbar_hide_different_desktop = boolean (0 or 1)\fR : If enabled, the taskbar shows only the tasks from the current desktop. Useful to make multi\-desktop taskbars more compact, but still allow desktop switching with mouse click. \fI(since 1.0)\fP
|
||||
.IP \(bu 2
|
||||
\fB\fCtaskbar_always_show_all_desktop_tasks = boolean (0 or 1)\fR : Has effect only if \fB\fCtaskbar_mode = multi_desktop\fR\&. If enabled, tasks that appear on all desktops are shown on all taskbars. Otherwise, they are shown only on the taskbar of the current desktop. \fI(since 0.12.4)\fP
|
||||
.IP \(bu 2
|
||||
\fB\fCtaskbar_sort_order = none/title/center\fR : Specifies the sort order of the tasks on the taskbar. \fI(since 0.12)\fP
|
||||
@@ -506,6 +512,10 @@ The following options configure the task buttons in the taskbar:
|
||||
.IP \(bu 2
|
||||
\fB\fCtask_tooltip = boolean (0 or 1)\fR : Whether to show tooltips for tasks.
|
||||
.IP \(bu 2
|
||||
\fB\fCtask_thumbnail = boolean (0 or 1)\fR : Whether to show thumbnail tooltips for tasks. \fI(since 16.0)\fP
|
||||
.IP \(bu 2
|
||||
\fB\fCtask_thumbnail_size = width\fR : Thumbnail size. \fI(since 16.0)\fP
|
||||
.IP \(bu 2
|
||||
\fB\fCtask_maximum_size = width height\fR
|
||||
.RS
|
||||
.IP \(bu 2
|
||||
@@ -578,7 +588,7 @@ The action semantics:
|
||||
.IP \(bu 2
|
||||
\fB\fCsystray_icon_asb = alpha (0 to 100) saturation (\-100 to 100) brightness (\-100 to 100)\fR : Adjust the systray icons color and transparency.
|
||||
.IP \(bu 2
|
||||
\fB\fCsystray_monitor = integer (1, 2, ...)\fR : On which monitor to draw the systray. The first monitor is \fB\fC1\fR\&. \fI(since 0.12)\fP
|
||||
\fB\fCsystray_monitor = integer (1, 2, ...) or primary\fR : On which monitor to draw the systray. The first monitor is \fB\fC1\fR\&. \fI(since 0.12)\fP
|
||||
.IP \(bu 2
|
||||
\fB\fCsystray_name_filter = string\fR : Regular expression to identify icon names to be hidden. For example, \fB\fC^audacious$\fR will hide icons with the exact name \fB\fCaudacious\fR, while \fB\fCaud\fR will hide any icons having \fB\fCaud\fR in the name. \fI(since 0.13.1)\fP
|
||||
.RE
|
||||
@@ -651,12 +661,30 @@ To hide the clock, comment \fB\fCtime1_format\fR and \fB\fCtime2_format\fR\&.
|
||||
.IP \(bu 2
|
||||
\fB\fCbattery_low_cmd = notify\-send "battery low"\fR : Command to execute when the battery is low.
|
||||
.IP \(bu 2
|
||||
\fB\fCbattery_full_cmd = notify\-send "battery full"\fR : Command to execute when the battery is full.
|
||||
.IP \(bu 2
|
||||
\fB\fCbat1_font = [FAMILY\-LIST] [STYLE\-OPTIONS] [SIZE]\fR
|
||||
.IP \(bu 2
|
||||
\fB\fCbat2_font = [FAMILY\-LIST] [STYLE\-OPTIONS] [SIZE]\fR
|
||||
.IP \(bu 2
|
||||
\fB\fCbattery_font_color = color opacity (0 to 100)\fR
|
||||
.IP \(bu 2
|
||||
\fB\fCbat1_format = FORMAT_STRING\fR : Format for battery line 1. Default: %p. \fI(since 1.0)\fP Format specification:
|
||||
.RS
|
||||
.IP \(bu 2
|
||||
%s: State (charging, discharging, full, unknown).
|
||||
.IP \(bu 2
|
||||
%m: Minutes left until completely charged/discharged (estimated).
|
||||
.IP \(bu 2
|
||||
%h: Hours left until completely charged/discharged (estimated).
|
||||
.IP \(bu 2
|
||||
%t: Time left. Shows "hrs:mins" when charging/discharging, or "Ful\[rs]" when full.
|
||||
.IP \(bu 2
|
||||
%p: Percentage. Includes the % sign.
|
||||
.RE
|
||||
.IP \(bu 2
|
||||
\fB\fCbat2_format = FORMAT_STRING\fR : Format for battery line 2. Default: %t. \fI(since 1.0)\fP
|
||||
.IP \(bu 2
|
||||
\fB\fCbattery_padding = horizontal_padding vertical_padding\fR
|
||||
.IP \(bu 2
|
||||
\fB\fCbattery_background_id = integer\fR : Which background to use for the battery.
|
||||
@@ -696,7 +724,7 @@ To hide the clock, comment \fB\fCtime1_format\fR and \fB\fCtime2_format\fR\&.
|
||||
.IP \(bu 2
|
||||
\fB\fCexecp_icon_h = integer\fR : See \fB\fCexecp_icon_w\fR\&. \fI(since 0.12.4)\fP
|
||||
.IP \(bu 2
|
||||
\fB\fCexecp_tooltip = text\fR : The tooltip. Leave it empty to not display a tooltip. Not specifying this option leads to showing an automatically generated tooltip with information about when the command was last executed. \fI(since 0.12.4)\fP
|
||||
\fB\fCexecp_tooltip = text\fR : The tooltip. If left empty, no tooltip is displayed. If missing, the standard error of the command is shown as a tooltip (an ANSI clear screen sequence can reset the contents, bash: \fB\fCprintf '\\e[2J'\fR, C: \fB\fCprintf("\\x1b[2J");\fR). If the standard error is empty, the tooltip will show information about the time when the command was last executed. \fI(since 0.12.4)\fP
|
||||
.IP \(bu 2
|
||||
\fB\fCexecp_font = [FAMILY\-LIST] [STYLE\-OPTIONS] [SIZE]\fR : The font used to draw the text. \fI(since 0.12.4)\fP
|
||||
.IP \(bu 2
|
||||
@@ -875,3 +903,4 @@ and the wiki page at \[la]https://gitlab.com/o9000/tint2/wikis/home\[ra]\&.
|
||||
.PP
|
||||
This documentation is also provided in HTML and Markdown format in the system's default location
|
||||
for documentation files, usually \fB\fC/usr/share/doc/tint2\fR or \fB\fC/usr/local/share/doc/tint2\fR\&.
|
||||
\&.
|
||||
|
||||
32
doc/tint2.md
32
doc/tint2.md
@@ -1,4 +1,4 @@
|
||||
# TINT2 1 "2017-04-29" 0.14.4
|
||||
# TINT2 1 "2017-12-30" 16.1
|
||||
|
||||
## NAME
|
||||
tint2 - lightweight panel/taskbar
|
||||
@@ -119,6 +119,10 @@ The tint2 config file starts with the options defining background elements with
|
||||
* `color` is specified in hex RGB, e.g. #ff0000 is red
|
||||
* `opacity` varies from (0 to 100), where 0 is fully transparent, 100 is fully opaque
|
||||
|
||||
* `border_content_tint_weight = integer` : Mixes the border color with the content color (for tasks, this is the average color of the window icon). Values must be between 0 (no mixing) and 100 (fully replaces the color). *(since 16.0)*
|
||||
|
||||
* `background_content_tint_weight = integer` : Mixes the background color with the content color (for tasks, this is the average color of the window icon). Values must be between 0 (no mixing) and 100 (fully replaces the color). *(since 16.0)*
|
||||
|
||||
You can define as many backgrounds as you want. For example, the following config defines two backgrounds:
|
||||
|
||||
```
|
||||
@@ -258,11 +262,11 @@ gradient_id_pressed = 2
|
||||
|
||||
For example, `panel_items = STC` will show the systray, the taskbar and the clock (from left to right).
|
||||
|
||||
* `panel_monitor = monitor (all or 1 or 2 or ...)` : Which monitor tint2 draws the panel on
|
||||
* `panel_monitor = monitor (all or primary or 1 or 2 or ...)` : Which monitor tint2 draws the panel on
|
||||
* The first monitor is `1`
|
||||
* Use `panel_monitor = all` to get a separate panel per monitor
|
||||
|
||||
* `primary_monitor_first = boolean (0 or 1)` : Place the primary monitor before all the other monitors in the list. *(since 0.12.4)*
|
||||
* `primary_monitor_first = boolean (0 or 1)` : Place the primary monitor before all the other monitors in the list. *(since 0.12.4; removed in 1.0, use `primary` instead)*
|
||||
|
||||

|
||||
|
||||
@@ -370,6 +374,8 @@ panel_size = 94% 30
|
||||
|
||||
* `taskbar_hide_different_monitor = boolean (0 or 1)` : If enabled, the taskbar shows only the tasks from the current monitor. Useful when running different tint2 instances on different monitors, each one having its own config. *(since 0.12)*
|
||||
|
||||
* `taskbar_hide_different_desktop = boolean (0 or 1)` : If enabled, the taskbar shows only the tasks from the current desktop. Useful to make multi-desktop taskbars more compact, but still allow desktop switching with mouse click. *(since 1.0)*
|
||||
|
||||
* `taskbar_always_show_all_desktop_tasks = boolean (0 or 1)` : Has effect only if `taskbar_mode = multi_desktop`. If enabled, tasks that appear on all desktops are shown on all taskbars. Otherwise, they are shown only on the taskbar of the current desktop. *(since 0.12.4)*
|
||||
|
||||
* `taskbar_sort_order = none/title/center` : Specifies the sort order of the tasks on the taskbar. *(since 0.12)*
|
||||
@@ -407,6 +413,10 @@ The following options configure the task buttons in the taskbar:
|
||||
|
||||
* `task_tooltip = boolean (0 or 1)` : Whether to show tooltips for tasks.
|
||||
|
||||
* `task_thumbnail = boolean (0 or 1)` : Whether to show thumbnail tooltips for tasks. *(since 16.0)*
|
||||
|
||||
* `task_thumbnail_size = width` : Thumbnail size. *(since 16.0)*
|
||||
|
||||
* `task_maximum_size = width height`
|
||||
* `width` is used with horizontal panels to limit the size of the tasks. Use `width = 0` to get full taskbar width.
|
||||
* `height` is used with vertical panels.
|
||||
@@ -471,7 +481,7 @@ The action semantics:
|
||||
|
||||
* `systray_icon_asb = alpha (0 to 100) saturation (-100 to 100) brightness (-100 to 100)` : Adjust the systray icons color and transparency.
|
||||
|
||||
* `systray_monitor = integer (1, 2, ...)` : On which monitor to draw the systray. The first monitor is `1`. *(since 0.12)*
|
||||
* `systray_monitor = integer (1, 2, ...) or primary` : On which monitor to draw the systray. The first monitor is `1`. *(since 0.12)*
|
||||
|
||||
* `systray_name_filter = string` : Regular expression to identify icon names to be hidden. For example, `^audacious$` will hide icons with the exact name `audacious`, while `aud` will hide any icons having `aud` in the name. *(since 0.13.1)*
|
||||
|
||||
@@ -534,12 +544,23 @@ The action semantics:
|
||||
|
||||
* `battery_low_cmd = notify-send "battery low"` : Command to execute when the battery is low.
|
||||
|
||||
* `battery_full_cmd = notify-send "battery full"` : Command to execute when the battery is full.
|
||||
|
||||
* `bat1_font = [FAMILY-LIST] [STYLE-OPTIONS] [SIZE]`
|
||||
|
||||
* `bat2_font = [FAMILY-LIST] [STYLE-OPTIONS] [SIZE]`
|
||||
|
||||
* `battery_font_color = color opacity (0 to 100)`
|
||||
|
||||
* `bat1_format = FORMAT_STRING` : Format for battery line 1. Default: %p. *(since 1.0)* Format specification:
|
||||
* %s: State (charging, discharging, full, unknown).
|
||||
* %m: Minutes left until completely charged/discharged (estimated).
|
||||
* %h: Hours left until completely charged/discharged (estimated).
|
||||
* %t: Time left. Shows "hrs:mins" when charging/discharging, or "Ful\" when full.
|
||||
* %p: Percentage. Includes the % sign.
|
||||
|
||||
* `bat2_format = FORMAT_STRING` : Format for battery line 2. Default: %t. *(since 1.0)*
|
||||
|
||||
* `battery_padding = horizontal_padding vertical_padding`
|
||||
|
||||
* `battery_background_id = integer` : Which background to use for the battery.
|
||||
@@ -578,7 +599,7 @@ The action semantics:
|
||||
|
||||
* `execp_icon_h = integer` : See `execp_icon_w`. *(since 0.12.4)*
|
||||
|
||||
* `execp_tooltip = text` : The tooltip. Leave it empty to not display a tooltip. Not specifying this option leads to showing an automatically generated tooltip with information about when the command was last executed. *(since 0.12.4)*
|
||||
* `execp_tooltip = text` : The tooltip. If left empty, no tooltip is displayed. If missing, the standard error of the command is shown as a tooltip (an ANSI clear screen sequence can reset the contents, bash: `printf '\e[2J'`, C: `printf("\x1b[2J");`). If the standard error is empty, the tooltip will show information about the time when the command was last executed. *(since 0.12.4)*
|
||||
|
||||
* `execp_font = [FAMILY-LIST] [STYLE-OPTIONS] [SIZE]` : The font used to draw the text. *(since 0.12.4)*
|
||||
|
||||
@@ -739,3 +760,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`.
|
||||
.
|
||||
|
||||
@@ -1,9 +1,13 @@
|
||||
#!/bin/sh
|
||||
|
||||
MAJOR=0.14
|
||||
SCRIPT_DIR=$(dirname "$0")
|
||||
DIRTY=""
|
||||
VERSION=""
|
||||
|
||||
if git status 1>/dev/null 2>/dev/null
|
||||
OLD_DIR=$(pwd)
|
||||
cd ${SCRIPT_DIR}
|
||||
|
||||
if [ -d .git ] && git status 1>/dev/null 2>/dev/null
|
||||
then
|
||||
git update-index -q --ignore-submodules --refresh
|
||||
# Disallow unstaged changes in the working tree
|
||||
@@ -31,15 +35,31 @@ then
|
||||
DIRTY="-dirty"
|
||||
fi
|
||||
fi
|
||||
VERSION=$(git describe --exact-match 2>/dev/null || echo "$MAJOR-git$(git show -s --pretty=format:%ci | cut -d ' ' -f 1 | tr -d '-').$(git show -s --pretty=format:%h)")$DIRTY
|
||||
else
|
||||
VERSION=$(head -n 1 ChangeLog || head -n 1 ../ChangeLog | cut -d ' ' -f 2)
|
||||
if [ $VERSION = "master" ]
|
||||
if git describe 1>/dev/null 2>/dev/null
|
||||
then
|
||||
VERSION=$VERSION-$(head -n 1 ChangeLog || head -n 1 ../ChangeLog | cut -d ' ' -f 1)
|
||||
VERSION=$(git describe 2>/dev/null)$DIRTY
|
||||
elif git log -n 1 1>/dev/null 2>/dev/null
|
||||
then
|
||||
VERSION=$(head -n 1 "ChangeLog" | cut -d ' ' -f 2)
|
||||
if [ "$VERSION" = "master" ]
|
||||
then
|
||||
PREVIOUS=$(grep '^2' "ChangeLog" | head -n 2 | tail -n 1 | cut -d ' ' -f 2)
|
||||
HASH=$(git log -n 1 --pretty=format:%cI.%ct.%h | tr -d ':' | tr -d '-' | tr '.' '-' | sed 's/T[0-9\+]*//g' 2>/dev/null)
|
||||
VERSION=$PREVIOUS-next-$HASH
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
|
||||
if [ -z "$VERSION" ]
|
||||
then
|
||||
VERSION=$(head -n 1 "ChangeLog" | cut -d ' ' -f 2)
|
||||
if [ "$VERSION" = "master" ]
|
||||
then
|
||||
VERSION=$VERSION-$(head -n 1 "ChangeLog" | cut -d ' ' -f 1)
|
||||
fi
|
||||
fi
|
||||
|
||||
cd "${OLD_DIR}"
|
||||
|
||||
VERSION=$(echo "$VERSION" | sed 's/^v//')
|
||||
|
||||
|
||||
118
new-release.py
118
new-release.py
@@ -76,37 +76,21 @@ def get_last_version():
|
||||
return tags[-1]
|
||||
|
||||
|
||||
def inc_version(v, major=False, minor=False, rc=False):
|
||||
if "-rc" in v:
|
||||
# v4.0-rc7 -> v4.0-rc8 or v4.0
|
||||
if minor:
|
||||
return v.split("-rc")[0]
|
||||
else:
|
||||
parts = v.split("-rc")
|
||||
parts[-1] = str(int(parts[-1]) + 1)
|
||||
return "-rc".join(parts)
|
||||
def inc_version(v, feature=False):
|
||||
if v.startswith("v0."):
|
||||
assert v == "v0.14.6"
|
||||
return "v15.0"
|
||||
# v4.11 -> v4.12 or v5.0
|
||||
parts = v.split(".")
|
||||
while len(parts) < 2:
|
||||
parts.append("0")
|
||||
assert len(parts) == 2
|
||||
if feature:
|
||||
parts[-2] = "v" + str(int(parts[-2].replace("v", "")) + 1)
|
||||
parts[-1] = "0"
|
||||
else:
|
||||
# v4.11 = v4, 11, 0 -> v4.11.1 or v4.12 or v.4.12-rc1 or v5.0 or v5.0-rc1
|
||||
# v4.11.7 = v4, 11, 7 -> ...
|
||||
parts = v.split(".")
|
||||
while len(parts) < 3:
|
||||
parts.append("0")
|
||||
assert len(parts) == 3
|
||||
if major:
|
||||
parts[-3] = "v" + str(int(parts[-3].replace("v", "")) + 1)
|
||||
parts[-2] = "0"
|
||||
if rc:
|
||||
parts[-2] += "-rc1"
|
||||
parts[-1] = ""
|
||||
elif minor or rc:
|
||||
parts[-2] = str(int(parts[-2]) + 1)
|
||||
if rc:
|
||||
parts[-2] += "-rc1"
|
||||
parts[-1] = ""
|
||||
else:
|
||||
parts[-1] = str(int(parts[-1]) + 1)
|
||||
assert not rc
|
||||
return ".".join([s for s in parts if s])
|
||||
parts[-1] = str(int(parts[-1]) + 1)
|
||||
return ".".join([s for s in parts if s])
|
||||
|
||||
|
||||
def assert_equal(a, b):
|
||||
@@ -117,53 +101,23 @@ def assert_equal(a, b):
|
||||
|
||||
def test_inc_version():
|
||||
# auto
|
||||
assert_equal(inc_version("v1.0"), "v1.0.1")
|
||||
assert_equal(inc_version("v1.0.1"), "v1.0.2")
|
||||
assert_equal(inc_version("v1.0.2"), "v1.0.3")
|
||||
assert_equal(inc_version("v1.0.10"), "v1.0.11")
|
||||
assert_equal(inc_version("v1.1.10"), "v1.1.11")
|
||||
assert_equal(inc_version("v1.1.10"), "v1.1.11")
|
||||
# rc
|
||||
assert_equal(inc_version("v1.0", False, False, True), "v1.1-rc1")
|
||||
assert_equal(inc_version("v1.0.1", False, False, True), "v1.1-rc1")
|
||||
assert_equal(inc_version("v1.0.2", False, False, True), "v1.1-rc1")
|
||||
assert_equal(inc_version("v1.0.10", False, False, True), "v1.1-rc1")
|
||||
assert_equal(inc_version("v1.1.10", False, False, True), "v1.2-rc1")
|
||||
assert_equal(inc_version("v1.1.10", False, False, True), "v1.2-rc1")
|
||||
# minor
|
||||
assert_equal(inc_version("v1.0", False, True, False), "v1.1")
|
||||
assert_equal(inc_version("v1.0.1", False, True, False), "v1.1")
|
||||
assert_equal(inc_version("v1.0.2", False, True, False), "v1.1")
|
||||
assert_equal(inc_version("v1.0.10", False, True, False), "v1.1")
|
||||
assert_equal(inc_version("v1.1.10", False, True, False), "v1.2")
|
||||
assert_equal(inc_version("v1.1.10", False, True, False), "v1.2")
|
||||
# minor rc
|
||||
assert_equal(inc_version("v1.0", False, True, True), "v1.1-rc1")
|
||||
assert_equal(inc_version("v1.0.1", False, True, True), "v1.1-rc1")
|
||||
assert_equal(inc_version("v1.0.2", False, True, True), "v1.1-rc1")
|
||||
assert_equal(inc_version("v1.0.10", False, True, True), "v1.1-rc1")
|
||||
assert_equal(inc_version("v1.1.10", False, True, True), "v1.2-rc1")
|
||||
assert_equal(inc_version("v1.1.10", False, True, True), "v1.2-rc1")
|
||||
# major rc
|
||||
assert_equal(inc_version("v1.0", True, False, True), "v2.0-rc1")
|
||||
assert_equal(inc_version("v1.0.1", True, False, True), "v2.0-rc1")
|
||||
assert_equal(inc_version("v1.0.2", True, False, True), "v2.0-rc1")
|
||||
assert_equal(inc_version("v1.0.10", True, False, True), "v2.0-rc1")
|
||||
assert_equal(inc_version("v1.1.10", True, False, True), "v2.0-rc1")
|
||||
assert_equal(inc_version("v1.1.10", True, False, True), "v2.0-rc1")
|
||||
# major
|
||||
assert_equal(inc_version("v1.0", True), "v2.0")
|
||||
assert_equal(inc_version("v1.0.1", True), "v2.0")
|
||||
assert_equal(inc_version("v1.0.2", True), "v2.0")
|
||||
assert_equal(inc_version("v1.0.10", True), "v2.0")
|
||||
assert_equal(inc_version("v1.1.10", True), "v2.0")
|
||||
assert_equal(inc_version("v1.1.10", True), "v2.0")
|
||||
# rc auto
|
||||
assert_equal(inc_version("v1.0-rc1"), "v1.0-rc2")
|
||||
assert_equal(inc_version("v1.1-rc2"), "v1.1-rc3")
|
||||
# rc minor
|
||||
assert_equal(inc_version("v1.0-rc1", False, True), "v1.0")
|
||||
assert_equal(inc_version("v1.1-rc2", False, True), "v1.1")
|
||||
assert_equal(inc_version("v0.14.6"), "v15.0")
|
||||
assert_equal(inc_version("v15"), "v15.1")
|
||||
assert_equal(inc_version("v15.0"), "v15.1")
|
||||
assert_equal(inc_version("v16.1"), "v16.2")
|
||||
assert_equal(inc_version("v16.10"), "v16.11")
|
||||
# fix
|
||||
assert_equal(inc_version("v0.14.6", False), "v15.0")
|
||||
assert_equal(inc_version("v15", False), "v15.1")
|
||||
assert_equal(inc_version("v15.0", False), "v15.1")
|
||||
assert_equal(inc_version("v16.1", False), "v16.2")
|
||||
assert_equal(inc_version("v16.10", False), "v16.11")
|
||||
# feature
|
||||
assert_equal(inc_version("v15", True), "v16.0")
|
||||
assert_equal(inc_version("v15.0", True), "v16.0")
|
||||
assert_equal(inc_version("v15.1", True), "v16.0")
|
||||
assert_equal(inc_version("v15.2", True), "v16.0")
|
||||
assert_equal(inc_version("v15.10", True), "v16.0")
|
||||
|
||||
|
||||
def replace_in_file(path, before, after):
|
||||
@@ -198,9 +152,7 @@ def update_log(path, version, date):
|
||||
|
||||
if __name__ == '__main__':
|
||||
parser = argparse.ArgumentParser()
|
||||
parser.add_argument("--major", action="store_true")
|
||||
parser.add_argument("--minor", action="store_true")
|
||||
parser.add_argument("--rc", action="store_true")
|
||||
parser.add_argument("--feature", action="store_true")
|
||||
parser.add_argument("--undo", 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)
|
||||
@@ -217,7 +169,7 @@ if __name__ == '__main__':
|
||||
os.system("git log -1")
|
||||
sys.exit(0)
|
||||
info("Old version:", old_version)
|
||||
version = inc_version(old_version, args.major, args.minor, args.rc)
|
||||
version = inc_version(old_version, args.feature)
|
||||
readable_version = version.replace("v", "")
|
||||
date = datetime.datetime.now().strftime("%Y-%m-%d")
|
||||
info("New version:", readable_version, version, date)
|
||||
@@ -238,3 +190,7 @@ if __name__ == '__main__':
|
||||
run("cd tint2-%s ; mkdir build ; cd build ; cmake .. ; make" % readable_version)
|
||||
assert_equal(run("./tint2-%s/build/tint2 -v" % readable_version).strip(), "tint2 version %s" % readable_version)
|
||||
os.system("git log -p -1 --word-diff")
|
||||
print "Does this look correct? [y/n]"
|
||||
choice = raw_input().lower()
|
||||
if choice != "y":
|
||||
run("git reset --hard HEAD~ ; git tag -d %s ; git tag -d %s" % (version, readable_version))
|
||||
|
||||
@@ -23,7 +23,8 @@ Homepage: https://gitlab.com/o9000/tint2/
|
||||
Package: tint2
|
||||
Architecture: any
|
||||
Depends: ${shlibs:Depends},
|
||||
${misc:Depends}
|
||||
${misc:Depends},
|
||||
procps
|
||||
Description: lightweight taskbar
|
||||
Tint is a simple panel/taskbar intentionally made for openbox3, but should
|
||||
also work with other window managers. The taskbar includes transparency and
|
||||
|
||||
2
packaging/debian/postinst
Normal file
2
packaging/debian/postinst
Normal file
@@ -0,0 +1,2 @@
|
||||
pkill -SIGUSR2 tint2 || true
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
dh $@
|
||||
|
||||
override_dh_auto_configure:
|
||||
dh_auto_configure -- -DCMAKE_BUILD_TYPE=RelWithDebInfo
|
||||
dh_auto_configure -- -DCMAKE_BUILD_TYPE=RelWithDebInfo -DCMAKE_INSTALL_SYSCONFDIR=/etc
|
||||
|
||||
override_dh_auto_install:
|
||||
dh_auto_install --destdir=$(CURDIR)/debian/tmp
|
||||
|
||||
@@ -21,7 +21,7 @@ then
|
||||
exit 1
|
||||
fi
|
||||
rm -f version.h
|
||||
VERSION=$(git describe --exact-match 2>/dev/null)
|
||||
VERSION=$(false 2>/dev/null)
|
||||
if [ $? -eq 0 ]
|
||||
then
|
||||
VERSION=$(echo "$VERSION" | sed 's/^v//')
|
||||
@@ -49,7 +49,7 @@ echo "echo \"#define VERSION_STRING \\\"$VERSION\\\"\" > version.h" > $DIR/get_v
|
||||
# Copy the debian files into the source directory
|
||||
cp -r debian $DIR/debian
|
||||
|
||||
for DISTRO in precise trusty xenial yakkety zesty
|
||||
for DISTRO in trusty xenial zesty artful bionic
|
||||
do
|
||||
# Cleanup from previous builds
|
||||
rm -rf tint2_$VERSION-*
|
||||
|
||||
23
packaging/update_version_status.sh
Executable file
23
packaging/update_version_status.sh
Executable file
@@ -0,0 +1,23 @@
|
||||
#!/bin/bash
|
||||
|
||||
export PATH=/usr/local/bin:/usr/bin:/bin:/usr/local/sbin:/usr/sbin:/sbin
|
||||
|
||||
set -e
|
||||
set -x
|
||||
|
||||
[ "${FLOCKER}" != "$0" ] && exec env FLOCKER="$0" flock -en "$0" "$0" "$@" || :
|
||||
|
||||
exec > ~/tint2.runner-version.log
|
||||
exec 2>&1
|
||||
|
||||
cd ~/tint2.wiki
|
||||
git reset --hard
|
||||
git pull
|
||||
|
||||
|
||||
~/tint2/packaging/version_status.py > packaging.tmp.md
|
||||
cat packaging.tmp.md > packaging.md
|
||||
rm packaging.tmp.md
|
||||
|
||||
git commit -am 'Update packaging info'
|
||||
git push origin master
|
||||
422
packaging/version_status.py
Executable file
422
packaging/version_status.py
Executable file
@@ -0,0 +1,422 @@
|
||||
#!/usr/bin/env python2
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
import sys
|
||||
reload(sys)
|
||||
sys.setdefaultencoding('utf8')
|
||||
import datetime
|
||||
import xml.etree.ElementTree as ET
|
||||
import ftplib
|
||||
import gzip
|
||||
import json
|
||||
import re
|
||||
from StringIO import StringIO
|
||||
import urllib2
|
||||
|
||||
|
||||
# Returns true if `value` is an integer represented as a string.
|
||||
def is_int(value):
|
||||
# type: (str) -> bool
|
||||
try:
|
||||
value = int(value)
|
||||
except ValueError:
|
||||
return False
|
||||
return True
|
||||
|
||||
|
||||
# Returns a new string with all instances of multiple whitespace
|
||||
# replaced with a single space.
|
||||
def collapse_multiple_spaces(line):
|
||||
# type: (str) -> str
|
||||
return " ".join(line.split())
|
||||
|
||||
|
||||
# Extracts the file name from a line of an FTP listing.
|
||||
# The input must be a valid directory entry (starting with "-" or "d").
|
||||
def ftp_file_name_from_listing(line):
|
||||
# type: (str) -> str
|
||||
line = collapse_multiple_spaces(line)
|
||||
return line.split(" ", 8)[-1]
|
||||
|
||||
|
||||
# Extracts a list of the directories and a list of the files
|
||||
# from an FTP listing.
|
||||
def ftp_list_dir_process_listing(lines):
|
||||
# type: (List[str]) -> List[str], List[str]
|
||||
dirs = []
|
||||
files = []
|
||||
for line in lines:
|
||||
if line.startswith("d"):
|
||||
dirs.append(ftp_file_name_from_listing(line))
|
||||
elif line.startswith("-"):
|
||||
files.append(ftp_file_name_from_listing(line))
|
||||
return dirs, files
|
||||
|
||||
|
||||
# Lists the remote FTP directory located at `path`.
|
||||
# Returns a list of the directories and a list of the files.
|
||||
def ftp_list_dir(ftp, path):
|
||||
# type: (ftplib.FTP, str) -> List[str], List[str]
|
||||
ftp.cwd(path)
|
||||
lines = []
|
||||
ftp.retrlines("LIST", lines.append)
|
||||
return ftp_list_dir_process_listing(lines)
|
||||
|
||||
|
||||
# Downloads a binary file to a string.
|
||||
# Returns the string.
|
||||
def ftp_download(ftp, path):
|
||||
# type: (ftplib.FTP, str) -> str
|
||||
blocks = []
|
||||
ftp.retrbinary("RETR {0}".format(path), blocks.append)
|
||||
return "".join(blocks)
|
||||
|
||||
|
||||
# Extracts the list of links from an HTML string.
|
||||
def http_links_from_listing(html):
|
||||
# type: (str) -> List[str]
|
||||
pattern = re.compile(r"""href=['"]+([^'"]+)['"]+""")
|
||||
return re.findall(pattern, html)
|
||||
|
||||
|
||||
# Extracts the list of paths (relative links, except to ../*) from an HTML string.
|
||||
def http_paths_from_listing(html):
|
||||
# type: (str) -> List[str]
|
||||
paths = []
|
||||
for link in http_links_from_listing(html):
|
||||
if link.startswith(".."):
|
||||
continue
|
||||
if link == "./" or link == "/":
|
||||
continue
|
||||
if "://" in link:
|
||||
continue
|
||||
paths.append(link)
|
||||
return paths
|
||||
|
||||
|
||||
# Downloads a file as string from an URL. Decodes correctly.
|
||||
def http_download_txt(url):
|
||||
# type: (str) -> str
|
||||
try:
|
||||
r = urllib2.urlopen(url)
|
||||
encoding = r.headers.getparam("charset")
|
||||
if not encoding:
|
||||
encoding = "utf-8"
|
||||
return r.read().decode(encoding)
|
||||
except:
|
||||
raise
|
||||
|
||||
|
||||
# Extracts the list of paths (relative links, except to ../*) from the HTML code
|
||||
# located at `url`.
|
||||
def http_list_dir(url):
|
||||
# type: (str) -> List[str]
|
||||
try:
|
||||
html = http_download_txt(url)
|
||||
except:
|
||||
return []
|
||||
return http_paths_from_listing(html)
|
||||
|
||||
|
||||
# Extracts the version and maintainer info for a package, from a Debian repository Packages file.
|
||||
def deb_packages_extract_version(packages, name):
|
||||
# type: (str, str) -> str, str
|
||||
inside = False
|
||||
version = None
|
||||
maintainer = None
|
||||
for line in packages.split("\n"):
|
||||
if line == "Package: " + name:
|
||||
inside = True
|
||||
elif not line:
|
||||
if inside:
|
||||
break
|
||||
else:
|
||||
if inside:
|
||||
if line.startswith("Version:"):
|
||||
version = line.split(":", 1)[-1].strip()
|
||||
elif line.startswith("Maintainer:"):
|
||||
maintainer = line.split(":", 1)[-1].strip()
|
||||
return version, maintainer
|
||||
|
||||
# Extracts the version and maintainer info for a package, from an Arch PKGBUILD file.
|
||||
def arch_pkgbuild_extract_version(pkgbuild):
|
||||
# type: (str) -> str, str
|
||||
version = None
|
||||
maintainer = None
|
||||
for line in pkgbuild.split("\n"):
|
||||
if line.startswith("# Maintainer:"):
|
||||
maintainer = line.split(":", 1)[-1].strip()
|
||||
elif line.startswith("pkgver="):
|
||||
version = line.split("=", 1)[-1].strip()
|
||||
return version, maintainer
|
||||
|
||||
|
||||
# Debian
|
||||
|
||||
def get_debian_release_version(release):
|
||||
data = http_download_txt("http://metadata.ftp-master.debian.org/changelogs/main/t/tint2/{0}_changelog".format(release))
|
||||
version = data.split("\n", 1)[0].split("(", 1)[-1].split(")", 1)[0].strip()
|
||||
maintainer = [line.split("--", 1)[-1] for line in data.split("\n") if line.startswith(" --")][0].split(" ")[0].strip()
|
||||
return release, version, maintainer
|
||||
|
||||
|
||||
def get_debian_versions():
|
||||
print >> sys.stderr, "Debian ..."
|
||||
return "Debian", "debian", [get_debian_release_version(release) for release in ["stable", "testing", "unstable", "experimental"]]
|
||||
|
||||
|
||||
# Ubuntu
|
||||
|
||||
def get_ubuntu_versions():
|
||||
print >> sys.stderr, "Ubuntu ..."
|
||||
data = http_download_txt("https://api.launchpad.net/1.0/ubuntu/+archive/primary?ws.op=getPublishedSources&source_name=tint2&exact_match=true")
|
||||
data = json.loads(data)["entries"]
|
||||
data.reverse()
|
||||
versions = []
|
||||
for package in data:
|
||||
if package["status"] == "Published":
|
||||
version = package["source_package_version"]
|
||||
release = package["distro_series_link"].split("/")[-1]
|
||||
maintainer = package["package_maintainer_link"]
|
||||
versions.append((release, version, maintainer))
|
||||
return "Ubuntu", "ubuntu", versions
|
||||
|
||||
|
||||
# BunsenLabs
|
||||
|
||||
def get_bunsenlabs_versions():
|
||||
print >> sys.stderr, "BunsenLabs ..."
|
||||
dirs = http_list_dir("https://eu.pkg.bunsenlabs.org/debian/dists/")
|
||||
versions = []
|
||||
for d in dirs:
|
||||
if d.endswith("/") and "/" not in d[:-1]:
|
||||
release = d.replace("/", "")
|
||||
packages = http_download_txt("https://eu.pkg.bunsenlabs.org/debian/dists/{0}/main/binary-amd64/Packages".format(release))
|
||||
version, maintainer = deb_packages_extract_version(packages, "tint2")
|
||||
if version:
|
||||
versions.append((release, version, maintainer))
|
||||
return "BunsenLabs", "bunsenlabs", versions
|
||||
|
||||
|
||||
# Arch
|
||||
|
||||
def get_arch_versions():
|
||||
print >> sys.stderr, "Arch ..."
|
||||
pkgbuild = http_download_txt("https://git.archlinux.org/svntogit/community.git/plain/trunk/PKGBUILD?h=packages/tint2")
|
||||
version, maintainer = arch_pkgbuild_extract_version(pkgbuild)
|
||||
return "Arch Linux", "archlinux", [("Community", version, maintainer)]
|
||||
|
||||
|
||||
# Fedora
|
||||
|
||||
def get_fedora_versions():
|
||||
print >> sys.stderr, "Fedora ..."
|
||||
dirs = http_list_dir("http://mirror.switch.ch/ftp/mirror/fedora/linux/development/")
|
||||
versions = []
|
||||
for d in dirs:
|
||||
if d.endswith("/") and "/" not in d[:-1]:
|
||||
release = d.replace("/", "")
|
||||
packages = http_list_dir("http://mirror.switch.ch/ftp/mirror/fedora/linux/development/{0}/Everything/source/tree/Packages/t/".format(release))
|
||||
for p in packages:
|
||||
if p.startswith("tint2-"):
|
||||
version = p.split("-", 1)[-1].split(".fc")[0]
|
||||
v = (release, version, "")
|
||||
if v not in versions:
|
||||
versions.append(v)
|
||||
return "Fedora", "fedora", versions
|
||||
|
||||
|
||||
# Red Hat (EPEL)
|
||||
|
||||
def get_redhat_epel_versions():
|
||||
print >> sys.stderr, "RedHat ..."
|
||||
dirs = http_list_dir("http://mirror.switch.ch/ftp/mirror/epel/")
|
||||
versions = []
|
||||
for d in dirs:
|
||||
if d.endswith("/") and "/" not in d[:-1] and is_int(d[:-1]):
|
||||
release = d.replace("/", "")
|
||||
packages = http_list_dir("http://mirror.switch.ch/ftp/mirror/epel/{0}/SRPMS/t/".format(release))
|
||||
for p in packages:
|
||||
if p.startswith("tint2-"):
|
||||
version = p.split("-", 1)[-1].split(".el")[0]
|
||||
v = (release, version, "")
|
||||
if v not in versions:
|
||||
versions.append(v)
|
||||
return "RedHat (EPEL)", "rhel", versions
|
||||
|
||||
|
||||
# SUSE
|
||||
|
||||
def get_suse_versions():
|
||||
print >> sys.stderr, "Suse ..."
|
||||
ftp = ftplib.FTP("mirror.switch.ch")
|
||||
ftp.login()
|
||||
releases, _ = ftp_list_dir(ftp, "/mirror/opensuse/opensuse/ports/update/leap/")
|
||||
versions = []
|
||||
for release in releases:
|
||||
root = "/mirror/opensuse/opensuse/ports/update/leap/{0}/oss/repodata/".format(release)
|
||||
_, files = ftp_list_dir(ftp, root)
|
||||
for fname in files:
|
||||
if fname.endswith("-primary.xml.gz"):
|
||||
data = ftp_download(ftp, "{0}/{1}".format(root, fname))
|
||||
xml = gzip.GzipFile(fileobj=StringIO(data)).read()
|
||||
root = ET.fromstring(xml)
|
||||
for package in root.findall("{http://linux.duke.edu/metadata/common}package"):
|
||||
name = package.find("{http://linux.duke.edu/metadata/common}name").text
|
||||
if name == "tint2":
|
||||
version = package.find("{http://linux.duke.edu/metadata/common}version").get("ver")
|
||||
versions.append((release, version, ""))
|
||||
ftp.quit()
|
||||
return "OpenSUSE", "opensuse", versions
|
||||
|
||||
|
||||
# Gentoo
|
||||
|
||||
def get_gentoo_versions():
|
||||
print >> sys.stderr, "Gentoo ..."
|
||||
files = http_list_dir("https://gitweb.gentoo.org/repo/gentoo.git/tree/x11-misc/tint2")
|
||||
versions = []
|
||||
for f in files:
|
||||
if "tint2" in f and f.endswith(".ebuild"):
|
||||
version = f.split("tint2-")[-1].split(".ebuild")[0]
|
||||
v = ("", version, "")
|
||||
if v not in versions:
|
||||
versions.append(v)
|
||||
return "Gentoo", "gentoo", versions
|
||||
|
||||
|
||||
# Void
|
||||
|
||||
def get_void_versions():
|
||||
print >> sys.stderr, "Void ..."
|
||||
template = http_download_txt("https://raw.githubusercontent.com/voidlinux/void-packages/master/srcpkgs/tint2/template")
|
||||
versions = []
|
||||
version = None
|
||||
maintainer = None
|
||||
for line in template.split("\n"):
|
||||
if line.startswith("version="):
|
||||
version = line.split("=", 1)[-1].replace('"', "").strip()
|
||||
elif line.startswith("maintainer="):
|
||||
maintainer = line.split("=", 1)[-1].replace('"', "").strip()
|
||||
if version:
|
||||
versions.append(("", version, maintainer))
|
||||
return "Void Linux", "void", versions
|
||||
|
||||
|
||||
# Alpine
|
||||
|
||||
def get_alpine_versions():
|
||||
print >> sys.stderr, "Alpine ..."
|
||||
apkbuild = http_download_txt("https://git.alpinelinux.org/cgit/aports/plain/community/tint2/APKBUILD")
|
||||
versions = []
|
||||
version = None
|
||||
maintainer = None
|
||||
for line in apkbuild.split("\n"):
|
||||
if line.startswith("pkgver="):
|
||||
version = line.split("=", 1)[-1].replace('"', "").strip()
|
||||
elif line.startswith("# Maintainer:"):
|
||||
maintainer = line.split(":", 1)[-1].replace('"', "").strip()
|
||||
if version:
|
||||
versions.append(("", version, maintainer))
|
||||
return "Alpine Linux", "alpine", versions
|
||||
|
||||
|
||||
# Slackware
|
||||
|
||||
def get_slack_versions():
|
||||
print >> sys.stderr, "Slackware ..."
|
||||
dirs = http_list_dir("https://slackbuilds.org/slackbuilds/")
|
||||
versions = []
|
||||
for d in dirs:
|
||||
if d.endswith("/") and "/" not in d[:-1]:
|
||||
release = d.replace("/", "")
|
||||
try:
|
||||
info = http_download_txt("https://slackbuilds.org/slackbuilds/{0}/desktop/tint2/tint2.info".format(release))
|
||||
except:
|
||||
continue
|
||||
version = None
|
||||
maintainer = None
|
||||
for line in info.split("\n"):
|
||||
if line.startswith("VERSION="):
|
||||
version = line.split("=", 1)[-1].replace('"', "").strip()
|
||||
elif line.startswith("MAINTAINER="):
|
||||
maintainer = line.split("=", 1)[-1].replace('"', "").strip()
|
||||
if version:
|
||||
versions.append((release, version, maintainer))
|
||||
return "Slackware", "slackware", versions
|
||||
|
||||
# FreeBSD
|
||||
|
||||
def get_freebsd_versions():
|
||||
print >> sys.stderr, "FreeBSD ..."
|
||||
makefile = http_download_txt("https://svnweb.freebsd.org/ports/head/x11/tint/Makefile?view=co")
|
||||
versions = []
|
||||
version = None
|
||||
maintainer = None
|
||||
for line in makefile.split("\n"):
|
||||
if line.startswith("PORTVERSION="):
|
||||
version = line.split("=", 1)[-1].strip()
|
||||
elif line.startswith("MAINTAINER="):
|
||||
maintainer = line.split("=", 1)[-1].strip()
|
||||
if version:
|
||||
versions.append(("", version, maintainer))
|
||||
return "FreeBSD", "freebsd", versions
|
||||
|
||||
|
||||
# OpenBSD
|
||||
|
||||
def get_openbsd_versions():
|
||||
print >> sys.stderr, "OpenBSD ..."
|
||||
makefile = http_download_txt("http://cvsweb.openbsd.org/cgi-bin/cvsweb/~checkout~/ports/x11/tint2/Makefile?content-type=text/plain")
|
||||
versions = []
|
||||
version = None
|
||||
for line in makefile.split("\n"):
|
||||
if line.startswith("V="):
|
||||
version = line.split("=", 1)[-1].strip()
|
||||
if version:
|
||||
versions.append(("", version, ""))
|
||||
return "OpenBSD", "openbsd", versions
|
||||
|
||||
|
||||
# Upstream
|
||||
|
||||
def get_tint2_version():
|
||||
print >> sys.stderr, "Upstream ..."
|
||||
readme = http_download_txt("https://gitlab.com/o9000/tint2/raw/master/README.md")
|
||||
version = readme.split("\n", 1)[0].split(":", 1)[-1].strip()
|
||||
return version
|
||||
|
||||
|
||||
def main():
|
||||
latest = get_tint2_version()
|
||||
distros = []
|
||||
distros.append(get_debian_versions())
|
||||
distros.append(get_bunsenlabs_versions())
|
||||
distros.append(get_ubuntu_versions())
|
||||
distros.append(get_fedora_versions())
|
||||
distros.append(get_redhat_epel_versions())
|
||||
#distros.append(get_suse_versions())
|
||||
distros.append(get_alpine_versions())
|
||||
distros.append(get_slack_versions())
|
||||
distros.append(get_arch_versions())
|
||||
distros.append(get_void_versions())
|
||||
distros.append(get_gentoo_versions())
|
||||
distros.append(get_freebsd_versions())
|
||||
distros.append(get_openbsd_versions())
|
||||
print "| Distribution | Release | Version | Status |"
|
||||
print "| ------------ | ------- | ------- | ------ |"
|
||||
for dist, dcode, releases in distros:
|
||||
icon = "".format(dcode)
|
||||
for r in releases:
|
||||
if r[1].split("-", 1)[0] == latest:
|
||||
status = ":white_check_mark: Latest"
|
||||
else:
|
||||
status = ":warning: Out of date"
|
||||
print "| {0} {1} | {2} | {3} | {4} |".format(icon, dist, r[0], r[1], status)
|
||||
utc_datetime = datetime.datetime.utcnow()
|
||||
print ""
|
||||
print "Last updated:", utc_datetime.strftime("%Y-%m-%d %H:%M UTC")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
208
packaging/version_status_test.py
Executable file
208
packaging/version_status_test.py
Executable file
@@ -0,0 +1,208 @@
|
||||
#!/usr/bin/env python2
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
import unittest
|
||||
from version_status import *
|
||||
|
||||
|
||||
class TestStringFunctions(unittest.TestCase):
|
||||
def test_collapse_multiple_spaces(self):
|
||||
self.assertEqual(collapse_multiple_spaces("asdf"), "asdf")
|
||||
self.assertEqual(collapse_multiple_spaces("as df"), "as df")
|
||||
self.assertEqual(collapse_multiple_spaces("as df"), "as df")
|
||||
self.assertEqual(collapse_multiple_spaces("a s d f"), "a s d f")
|
||||
|
||||
|
||||
class TestFtpFunctions(unittest.TestCase):
|
||||
def test_ftp_file_name_from_listing(self):
|
||||
self.assertEqual(ftp_file_name_from_listing("-rw-rw-r-- 1 1176 1176 1063 Jun 15 10:18 README"), "README")
|
||||
self.assertEqual(ftp_file_name_from_listing("-rw-rw-r-- 1 1176 1176 1063 Jun 15 10:18:12 README"), "README")
|
||||
self.assertEqual(ftp_file_name_from_listing("-rw-rw-r-- 1 1176 1176 1063 Jun 15 10:18 READ ME"), "READ ME")
|
||||
self.assertEqual(ftp_file_name_from_listing("-rw-rw-r-- 1 1176 1176 1063 Jun 15 10:18:12 READ ME"), "READ ME")
|
||||
self.assertEqual(ftp_file_name_from_listing("-rw-rw-r-- 1 1176 1176 1063 Jun 15 10:18 README"), "README")
|
||||
self.assertEqual(ftp_file_name_from_listing("-rw-rw-r-- 1 1176 1176 1063 Jun 15 10:18:12 README.txt"), "README.txt")
|
||||
self.assertEqual(ftp_file_name_from_listing("-rw-rw-r-- 1 1176 1176 1063 Jun 15 10:18:12 READ ME.txt"), "READ ME.txt")
|
||||
|
||||
def test_ftp_list_dir_process_listing(self):
|
||||
lines = [ "-rw-rw-r-- 1 1176 1176 1063 Jun 15 10:18 README",
|
||||
"-rw-rw-r-- 1 1176 1176 1063 Jun 15 10:18:11 READ ME.txt",
|
||||
"drwxr-sr-x 5 1176 1176 4096 Dec 19 2000 pool",
|
||||
"drwxr-sr-x 4 1176 1176 4096 Nov 17 2008 project",
|
||||
"drwxr-xr-x 3 1176 1176 4096 Oct 10 2012 tools"]
|
||||
dirs_check = ["pool", "project", "tools"]
|
||||
files_check = ["README", "READ ME.txt"]
|
||||
dirs, files = ftp_list_dir_process_listing(lines)
|
||||
dirs.sort()
|
||||
dirs_check.sort()
|
||||
files.sort()
|
||||
files_check.sort()
|
||||
self.assertEqual(dirs, dirs_check)
|
||||
self.assertEqual(files, files_check)
|
||||
|
||||
|
||||
class TestHttpFunctions(unittest.TestCase):
|
||||
def test_http_links_from_listing(self):
|
||||
html = """<html>
|
||||
<head><title>Index of /debian/dists/</title></head>
|
||||
<body bgcolor="white">
|
||||
<a href="http://google.com">google/</a>
|
||||
<h1>Index of /debian/dists/</h1><hr><pre><a href="../">../</a>
|
||||
<a href="bunsen-hydrogen/">bunsen-hydrogen/</a> 08-May-2017 20:31 -
|
||||
<a href="jessie-backports/">jessie-backports/</a> 01-Jul-2017 15:58 -
|
||||
<a href="unstable/">unstable/</a> 12-Aug-2017 19:32 -
|
||||
</pre><hr></body>
|
||||
</html>"""
|
||||
links_check = ["../", "bunsen-hydrogen/", "jessie-backports/", "unstable/", "http://google.com"]
|
||||
links = http_links_from_listing(html)
|
||||
links.sort()
|
||||
links_check.sort()
|
||||
self.assertEqual(links, links_check)
|
||||
|
||||
def test_http_paths_from_listing(self):
|
||||
html = """<html>
|
||||
<head><title>Index of /debian/dists/</title></head>
|
||||
<body bgcolor="white">
|
||||
<h1>Index of /debian/dists/</h1><hr><pre><a href="../">../</a>
|
||||
<a href="bunsen-hydrogen/">bunsen-hydrogen/</a> 08-May-2017 20:31 -
|
||||
<a href="jessie-backports/">jessie-backports/</a> 01-Jul-2017 15:58 -
|
||||
<a href="unstable/">unstable/</a> 12-Aug-2017 19:32 -
|
||||
</pre><hr></body>
|
||||
</html>"""
|
||||
paths_check = ["bunsen-hydrogen/", "jessie-backports/", "unstable/"]
|
||||
paths = http_paths_from_listing(html)
|
||||
paths.sort()
|
||||
paths_check.sort()
|
||||
self.assertEqual(paths, paths_check)
|
||||
|
||||
|
||||
class TestPackageFunctions(unittest.TestCase):
|
||||
def test_deb_packages_extract_version(self):
|
||||
packages = """Package: sendmailanalyzer
|
||||
Version: 9.2-1.1
|
||||
Architecture: all
|
||||
Maintainer: Dominique Fournier <dominique@fournier38.fr>
|
||||
Installed-Size: 749
|
||||
Pre-Depends: perl
|
||||
Depends: apache2
|
||||
Homepage: http://sareport.darold.net/
|
||||
Priority: optional
|
||||
Section: mail
|
||||
Filename: pool/main/s/sendmailanalyzer/sendmailanalyzer_9.2-1.1_all.deb
|
||||
Size: 143576
|
||||
SHA256: 0edcbde19a23333c8c894e27af32447582b38e3ccd84122ac07720fdaab8fa0c
|
||||
SHA1: a7f4dcf42e850acf2c201bc4594cb6b765dced20
|
||||
MD5sum: adb39196fc33a826b24e9d0e440cba25
|
||||
Description: Perl Sendmail/Postfix log analyser
|
||||
SendmailAnalyzer continuously read your mail log file to generate
|
||||
periodical HTML and graph reports. All reports are shown through
|
||||
a CGI web interface.
|
||||
It reports all you ever wanted to know about email trafic on your network.
|
||||
You can also use it in ISP environment with per domain report.
|
||||
|
||||
Package: tint2
|
||||
Version: 0.14.6-1
|
||||
Architecture: amd64
|
||||
Maintainer: Jens John <dev@2ion.de>
|
||||
Installed-Size: 1230
|
||||
Depends: libatk1.0-0 (>= 1.12.4), libc6 (>= 2.15), libcairo2 (>= 1.2.4), libfontconfig1 (>= 2.11), libfreetype6 (>= 2.2.1), libgdk-pixbuf2.0-0 (>= 2.22.0), libglib2.0-0 (>= 2.35.9), libgtk2.0-0 (>= 2.14.0), libimlib2 (>= 1.4.5), libpango-1.0-0 (>= 1.20.0), libpangocairo-1.0-0 (>= 1.14.0), libpangoft2-1.0-0 (>= 1.14.0), librsvg2-2 (>= 2.14.4), libstartup-notification0 (>= 0.4), libx11-6, libxcomposite1 (>= 1:0.3-1), libxdamage1 (>= 1:1.1), libxfixes3, libxinerama1, libxrandr2 (>= 2:1.2.99.3), libxrender1
|
||||
Homepage: https://gitlab.com/o9000/tint2/
|
||||
Priority: optional
|
||||
Section: x11
|
||||
Filename: pool/main/t/tint2/tint2_0.14.6-1_amd64.deb
|
||||
Size: 279638
|
||||
SHA256: c96e745425a97828952e9e0277176fe68e2512056915560ac968a66c88a0a8b7
|
||||
SHA1: 82edd60429a494bb127e6d8a10434fca0ee60f61
|
||||
MD5sum: 65455638fb41503361560b25a70b33b7
|
||||
Description: lightweight taskbar
|
||||
Tint is a simple panel/taskbar intentionally made for openbox3, but should
|
||||
also work with other window managers. The taskbar includes transparency and
|
||||
color settings for the font, icons, border, and background. It also supports
|
||||
multihead setups, customized mouse actions, and a built-in clock. Tint was
|
||||
originally based on ttm code. Since then, support has also been added
|
||||
for a battery monitor and system tray.
|
||||
.
|
||||
The goal is to keep a clean and unintrusive look with lightweight code and
|
||||
compliance with freedesktop specification.
|
||||
|
||||
Package: xfce4-power-manager
|
||||
Version: 1.4.4-4~bpo8+1
|
||||
Architecture: amd64
|
||||
Maintainer: Debian Xfce Maintainers <pkg-xfce-devel@lists.alioth.debian.org>
|
||||
Installed-Size: 541
|
||||
Depends: libc6 (>= 2.4), libcairo2 (>= 1.2.4), libdbus-1-3 (>= 1.0.2), libdbus-glib-1-2 (>= 0.88), libgdk-pixbuf2.0-0 (>= 2.22.0), libglib2.0-0 (>= 2.41.1), libgtk2.0-0 (>= 2.24.0), libnotify4 (>= 0.7.0), libpango-1.0-0 (>= 1.14.0), libpangocairo-1.0-0 (>= 1.14.0), libupower-glib3 (>= 0.99.0), libx11-6, libxext6, libxfce4ui-1-0 (>= 4.9.0), libxfce4util6 (>= 4.9.0), libxfconf-0-2 (>= 4.6.0), libxrandr2 (>= 2:1.2.99.2), upower (>= 0.99), xfce4-power-manager-data (= 1.4.4-4~bpo8+1)
|
||||
Recommends: libpam-systemd, xfce4-power-manager-plugins
|
||||
Homepage: http://goodies.xfce.org/projects/applications/xfce4-power-manager
|
||||
Priority: optional
|
||||
Section: xfce
|
||||
Filename: pool/main/x/xfce4-power-manager/xfce4-power-manager_1.4.4-4~bpo8+1_amd64.deb
|
||||
Size: 214122
|
||||
SHA256: 992b606afe5e9934bce19a1df2b8d7067c98b9d64e23a9b63dbd0c4cf28b4ac9
|
||||
SHA1: 6bfcd77071f31577a37abab063bf21a34f4d616c
|
||||
MD5sum: fb777aecbbfe39742649b768eb22c697
|
||||
Description: power manager for Xfce desktop
|
||||
This power manager for the Xfce desktop enables laptop users to set up
|
||||
a power profile for two different modes "on battery power" and "on ac
|
||||
power" while still allowing desktop users to at least change the DPMS
|
||||
settings and CPU frequency using the settings dialogue..
|
||||
.
|
||||
It features:
|
||||
* battery monitoring
|
||||
* cpu frequency settings
|
||||
* monitor DPMS settings
|
||||
* suspend/Hibernate
|
||||
* LCD brightness control
|
||||
* Lid, sleep and power switches control"""
|
||||
version, maintainer = deb_packages_extract_version(packages, "tint2")
|
||||
self.assertEqual(version, "0.14.6-1")
|
||||
self.assertEqual(maintainer, "Jens John <dev@2ion.de>")
|
||||
version, maintainer = deb_packages_extract_version(packages, "asdf")
|
||||
self.assertEqual(version, None)
|
||||
self.assertEqual(maintainer, None)
|
||||
|
||||
def test_arch_packages_extract_version(self):
|
||||
pkgbuild = """# $Id$
|
||||
# Maintainer: Alexander F Rødseth <xyproto@archlinux.org>
|
||||
# Contributor: Blue Peppers <bluepeppers@archlinux.us>
|
||||
# Contributor: Stefan Husmann <stefan-husmann@t-online.de>
|
||||
# Contributor: Yannick LM <LMyannicklm1337@gmail.com>
|
||||
|
||||
pkgname=tint2
|
||||
pkgver=0.14.6
|
||||
pkgrel=2
|
||||
pkgdesc='Basic, good-looking task manager for WMs'
|
||||
arch=('x86_64' 'i686')
|
||||
url='https://gitlab.com/o9000/tint2'
|
||||
license=('GPL2')
|
||||
depends=('gtk2' 'imlib2' 'startup-notification')
|
||||
makedepends=('cmake' 'startup-notification' 'git' 'ninja' 'setconf')
|
||||
source=("$pkgname-$pkgver.tar.bz2::https://gitlab.com/o9000/tint2/repository/archive.tar.bz2?ref=$pkgver")
|
||||
sha256sums=('b40079fb187aa248cd3b6957076f138d040c723b309e1b254ac0c8ec9826a451')
|
||||
|
||||
prepare() {
|
||||
mv "$pkgname-$pkgver-"* "$pkgname"
|
||||
setconf "$pkgname/get_version.sh" VERSION "$pkgver"
|
||||
}
|
||||
|
||||
build() {
|
||||
mkdir -p build
|
||||
cd build
|
||||
cmake "../$pkgname" \
|
||||
-DCMAKE_INSTALL_PREFIX=/usr \
|
||||
-DENABLE_TINT2CONF=1 \
|
||||
-GNinja
|
||||
ninja
|
||||
}
|
||||
|
||||
package() {
|
||||
DESTDIR="$pkgdir" ninja -C build install
|
||||
}
|
||||
|
||||
# getver: gitlab.com/o9000/tint2/blob/master/README.md
|
||||
# vim: ts=2 sw=2 et:"""
|
||||
version, maintainer = arch_pkgbuild_extract_version(pkgbuild)
|
||||
self.assertEqual(version, "0.14.6")
|
||||
self.assertEqual(maintainer, "Alexander F Rødseth <xyproto@archlinux.org>")
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
||||
@@ -35,20 +35,25 @@ gboolean bat1_has_font;
|
||||
PangoFontDescription *bat1_font_desc;
|
||||
gboolean bat2_has_font;
|
||||
PangoFontDescription *bat2_font_desc;
|
||||
char *bat1_format;
|
||||
char *bat2_format;
|
||||
struct BatteryState battery_state;
|
||||
gboolean battery_enabled;
|
||||
gboolean battery_tooltip_enabled;
|
||||
int percentage_hide;
|
||||
static timeout *battery_timeout;
|
||||
static Timer battery_timer;
|
||||
|
||||
static char buf_bat_percentage[10];
|
||||
static char buf_bat_time[20];
|
||||
#define BATTERY_BUF_SIZE 256
|
||||
static char buf_bat_line1[BATTERY_BUF_SIZE];
|
||||
static char buf_bat_line2[BATTERY_BUF_SIZE];
|
||||
|
||||
int8_t battery_low_status;
|
||||
gboolean battery_low_cmd_sent;
|
||||
gboolean battery_full_cmd_sent;
|
||||
char *ac_connected_cmd;
|
||||
char *ac_disconnected_cmd;
|
||||
char *battery_low_cmd;
|
||||
char *battery_full_cmd;
|
||||
char *battery_lclick_command;
|
||||
char *battery_mclick_command;
|
||||
char *battery_rclick_command;
|
||||
@@ -70,14 +75,18 @@ void default_battery()
|
||||
battery_found = FALSE;
|
||||
percentage_hide = 101;
|
||||
battery_low_cmd_sent = FALSE;
|
||||
battery_timeout = NULL;
|
||||
battery_full_cmd_sent = FALSE;
|
||||
INIT_TIMER(battery_timer);
|
||||
bat1_has_font = FALSE;
|
||||
bat1_font_desc = NULL;
|
||||
bat1_format = NULL;
|
||||
bat2_has_font = FALSE;
|
||||
bat2_font_desc = NULL;
|
||||
bat2_format = NULL;
|
||||
ac_connected_cmd = NULL;
|
||||
ac_disconnected_cmd = NULL;
|
||||
battery_low_cmd = NULL;
|
||||
battery_full_cmd = NULL;
|
||||
battery_lclick_command = NULL;
|
||||
battery_mclick_command = NULL;
|
||||
battery_rclick_command = NULL;
|
||||
@@ -98,6 +107,12 @@ void cleanup_battery()
|
||||
bat2_font_desc = NULL;
|
||||
free(battery_low_cmd);
|
||||
battery_low_cmd = NULL;
|
||||
free(battery_full_cmd);
|
||||
battery_full_cmd = NULL;
|
||||
free(bat1_format);
|
||||
bat1_format = NULL;
|
||||
free(bat2_format);
|
||||
bat2_format = NULL;
|
||||
free(battery_lclick_command);
|
||||
battery_lclick_command = NULL;
|
||||
free(battery_mclick_command);
|
||||
@@ -112,13 +127,97 @@ void cleanup_battery()
|
||||
ac_connected_cmd = NULL;
|
||||
free(ac_disconnected_cmd);
|
||||
ac_disconnected_cmd = NULL;
|
||||
stop_timeout(battery_timeout);
|
||||
battery_timeout = NULL;
|
||||
destroy_timer(&battery_timer);
|
||||
battery_found = FALSE;
|
||||
|
||||
battery_os_free();
|
||||
}
|
||||
|
||||
// Appends addendum to dest, and does not allow dest to grow over limit (including NULL terminator).
|
||||
char *strnappend(char *dest, const char *addendum, size_t limit)
|
||||
{
|
||||
char *tmp = strdup(dest);
|
||||
|
||||
// Actually concatenate them.
|
||||
snprintf(dest, limit, "%s%s", tmp, addendum);
|
||||
|
||||
free(tmp);
|
||||
return dest;
|
||||
}
|
||||
|
||||
void battery_update_text(char *dest, char *format)
|
||||
{
|
||||
if (!battery_enabled || !dest || !format)
|
||||
return;
|
||||
// We want to loop over the format specifier, replacing any known symbols with our battery data.
|
||||
// First, erase anything already stored in the buffer.
|
||||
// This ensures the string will always be null-terminated.
|
||||
bzero(dest, BATTERY_BUF_SIZE);
|
||||
|
||||
for (size_t o = 0; o < strlen(format); o++) {
|
||||
char buf[BATTERY_BUF_SIZE];
|
||||
bzero(buf, BATTERY_BUF_SIZE);
|
||||
char *c = &format[o];
|
||||
// Format specification:
|
||||
// %s : State (charging, discharging, full, unknown)
|
||||
// %m : Minutes left (estimated).
|
||||
// %h : Hours left (estimated).
|
||||
// %t : Time left. This is equivalent to the old behaviour; i.e. "(plugged in)" or "hrs:mins" otherwise.
|
||||
// %p : Percentage left. Includes the % sign.
|
||||
if (*c == '%') {
|
||||
c++;
|
||||
o++; // Skip the format control character.
|
||||
switch (*c) {
|
||||
case 's':
|
||||
// Append the appropriate status message to the string.
|
||||
strnappend(dest,
|
||||
(battery_state.state == BATTERY_CHARGING)
|
||||
? "Charging"
|
||||
: (battery_state.state == BATTERY_DISCHARGING)
|
||||
? "Discharging"
|
||||
: (battery_state.state == BATTERY_FULL)
|
||||
? "Full"
|
||||
: "Unknown",
|
||||
BATTERY_BUF_SIZE);
|
||||
break;
|
||||
case 'm':
|
||||
snprintf(buf, sizeof(buf), "%02d", battery_state.time.minutes);
|
||||
strnappend(dest, buf, BATTERY_BUF_SIZE);
|
||||
break;
|
||||
case 'h':
|
||||
snprintf(buf, sizeof(buf), "%02d", battery_state.time.hours);
|
||||
strnappend(dest, buf, BATTERY_BUF_SIZE);
|
||||
break;
|
||||
case 'p':
|
||||
snprintf(buf, sizeof(buf), "%d%%", battery_state.percentage);
|
||||
strnappend(dest, buf, BATTERY_BUF_SIZE);
|
||||
break;
|
||||
case 't':
|
||||
if (battery_state.state == BATTERY_FULL) {
|
||||
snprintf(buf, sizeof(buf), "Full");
|
||||
strnappend(dest, buf, BATTERY_BUF_SIZE);
|
||||
} else if (battery_state.time.hours > 0 && battery_state.time.minutes > 0) {
|
||||
snprintf(buf, sizeof(buf), "%02d:%02d", battery_state.time.hours, battery_state.time.minutes);
|
||||
strnappend(dest, buf, BATTERY_BUF_SIZE);
|
||||
}
|
||||
break;
|
||||
|
||||
case '%':
|
||||
case '\0':
|
||||
strnappend(dest, "%", BATTERY_BUF_SIZE);
|
||||
break;
|
||||
default:
|
||||
fprintf(stderr, "tint2: Battery: unrecognised format specifier '%%%c'.\n", *c);
|
||||
buf[0] = *c;
|
||||
strnappend(dest, buf, BATTERY_BUF_SIZE);
|
||||
}
|
||||
} else {
|
||||
buf[0] = *c;
|
||||
strnappend(dest, buf, BATTERY_BUF_SIZE);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void init_battery()
|
||||
{
|
||||
if (!battery_enabled)
|
||||
@@ -126,8 +225,7 @@ void init_battery()
|
||||
|
||||
battery_found = battery_os_init();
|
||||
|
||||
if (!battery_timeout)
|
||||
battery_timeout = add_timeout(10, 30000, update_battery_tick, 0, &battery_timeout);
|
||||
change_timer(&battery_timer, true, 10, 30000, update_battery_tick, 0);
|
||||
|
||||
update_battery();
|
||||
}
|
||||
@@ -169,6 +267,11 @@ void init_battery_panel(void *p)
|
||||
if (battery_tooltip_enabled)
|
||||
battery->area._get_tooltip_text = battery_get_tooltip;
|
||||
instantiate_area_gradients(&battery->area);
|
||||
|
||||
if (!bat1_format && !bat2_format) {
|
||||
bat1_format = strdup("%p");
|
||||
bat2_format = strdup("%t");
|
||||
}
|
||||
}
|
||||
|
||||
void battery_init_fonts()
|
||||
@@ -242,6 +345,16 @@ void update_battery_tick(void *arg)
|
||||
battery_low_cmd_sent = FALSE;
|
||||
}
|
||||
|
||||
if ((battery_state.percentage >= 100 || battery_state.state == BATTERY_FULL) &&
|
||||
!battery_full_cmd_sent) {
|
||||
tint_exec_no_sn(battery_full_cmd);
|
||||
battery_full_cmd_sent = TRUE;
|
||||
}
|
||||
if (battery_state.percentage < 100 && battery_state.state != BATTERY_FULL &&
|
||||
battery_full_cmd_sent) {
|
||||
battery_full_cmd_sent = FALSE;
|
||||
}
|
||||
|
||||
for (int i = 0; i < num_panels; i++) {
|
||||
// Show/hide if needed
|
||||
if (!battery_found) {
|
||||
@@ -278,158 +391,51 @@ int update_battery()
|
||||
battery_state.percentage = 100;
|
||||
}
|
||||
|
||||
battery_update_text(buf_bat_line1, bat1_format);
|
||||
if (bat2_format != 0) {
|
||||
battery_update_text(buf_bat_line2, bat2_format);
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
int battery_compute_desired_size(void *obj)
|
||||
{
|
||||
Battery *battery = (Battery *)obj;
|
||||
Panel *panel = (Panel *)battery->area.panel;
|
||||
int bat_percentage_height, bat_percentage_width, bat_percentage_height_ink;
|
||||
int bat_time_height, bat_time_width, bat_time_height_ink;
|
||||
|
||||
snprintf(buf_bat_percentage, sizeof(buf_bat_percentage), "%d%%", battery_state.percentage);
|
||||
if (battery_state.state == BATTERY_FULL) {
|
||||
strcpy(buf_bat_time, "Full");
|
||||
} else {
|
||||
snprintf(buf_bat_time, sizeof(buf_bat_time), "%02d:%02d", battery_state.time.hours, battery_state.time.minutes);
|
||||
}
|
||||
get_text_size2(bat1_font_desc,
|
||||
&bat_percentage_height_ink,
|
||||
&bat_percentage_height,
|
||||
&bat_percentage_width,
|
||||
panel->area.height,
|
||||
panel->area.width,
|
||||
buf_bat_percentage,
|
||||
strlen(buf_bat_percentage),
|
||||
PANGO_WRAP_WORD_CHAR,
|
||||
PANGO_ELLIPSIZE_NONE,
|
||||
FALSE);
|
||||
get_text_size2(bat2_font_desc,
|
||||
&bat_time_height_ink,
|
||||
&bat_time_height,
|
||||
&bat_time_width,
|
||||
panel->area.height,
|
||||
panel->area.width,
|
||||
buf_bat_time,
|
||||
strlen(buf_bat_time),
|
||||
PANGO_WRAP_WORD_CHAR,
|
||||
PANGO_ELLIPSIZE_NONE,
|
||||
FALSE);
|
||||
|
||||
if (panel_horizontal) {
|
||||
int new_size = (bat_percentage_width > bat_time_width) ? bat_percentage_width : bat_time_width;
|
||||
new_size += 2 * battery->area.paddingxlr + left_right_border_width(&battery->area);
|
||||
return new_size;
|
||||
} else {
|
||||
int new_size = bat_percentage_height + bat_time_height + 2 * battery->area.paddingxlr +
|
||||
top_bottom_border_width(&battery->area);
|
||||
return new_size;
|
||||
}
|
||||
return text_area_compute_desired_size(&battery->area, buf_bat_line1, buf_bat_line2, bat1_font_desc, bat2_font_desc);
|
||||
}
|
||||
|
||||
gboolean resize_battery(void *obj)
|
||||
{
|
||||
Battery *battery = (Battery *)obj;
|
||||
Panel *panel = (Panel *)battery->area.panel;
|
||||
int bat_percentage_height, bat_percentage_width, bat_percentage_height_ink;
|
||||
int bat_time_height, bat_time_width, bat_time_height_ink;
|
||||
int ret = 0;
|
||||
|
||||
snprintf(buf_bat_percentage, sizeof(buf_bat_percentage), "%d%%", battery_state.percentage);
|
||||
if (battery_state.state == BATTERY_FULL) {
|
||||
strcpy(buf_bat_time, "Full");
|
||||
} else {
|
||||
snprintf(buf_bat_time, sizeof(buf_bat_time), "%02d:%02d", battery_state.time.hours, battery_state.time.minutes);
|
||||
}
|
||||
get_text_size2(bat1_font_desc,
|
||||
&bat_percentage_height_ink,
|
||||
&bat_percentage_height,
|
||||
&bat_percentage_width,
|
||||
panel->area.height,
|
||||
panel->area.width,
|
||||
buf_bat_percentage,
|
||||
strlen(buf_bat_percentage),
|
||||
PANGO_WRAP_WORD_CHAR,
|
||||
PANGO_ELLIPSIZE_NONE,
|
||||
FALSE);
|
||||
get_text_size2(bat2_font_desc,
|
||||
&bat_time_height_ink,
|
||||
&bat_time_height,
|
||||
&bat_time_width,
|
||||
panel->area.height,
|
||||
panel->area.width,
|
||||
buf_bat_time,
|
||||
strlen(buf_bat_time),
|
||||
PANGO_WRAP_WORD_CHAR,
|
||||
PANGO_ELLIPSIZE_NONE,
|
||||
FALSE);
|
||||
|
||||
if (panel_horizontal) {
|
||||
int new_size = (bat_percentage_width > bat_time_width) ? bat_percentage_width : bat_time_width;
|
||||
new_size += 2 * battery->area.paddingxlr + left_right_border_width(&battery->area);
|
||||
if (new_size > battery->area.width || new_size < battery->area.width - 2) {
|
||||
// we try to limit the number of resize
|
||||
battery->area.width = new_size;
|
||||
battery->bat1_posy = (battery->area.height - bat_percentage_height - bat_time_height) / 2;
|
||||
battery->bat2_posy = battery->bat1_posy + bat_percentage_height;
|
||||
ret = 1;
|
||||
}
|
||||
} else {
|
||||
int new_size = bat_percentage_height + bat_time_height + 2 * battery->area.paddingxlr +
|
||||
top_bottom_border_width(&battery->area);
|
||||
if (new_size > battery->area.height || new_size < battery->area.height - 2) {
|
||||
battery->area.height = new_size;
|
||||
battery->bat1_posy = (battery->area.height - bat_percentage_height - bat_time_height - 2) / 2;
|
||||
battery->bat2_posy = battery->bat1_posy + bat_percentage_height + 2;
|
||||
ret = 1;
|
||||
}
|
||||
}
|
||||
|
||||
schedule_redraw(&battery->area);
|
||||
return ret;
|
||||
return resize_text_area(&battery->area,
|
||||
buf_bat_line1,
|
||||
buf_bat_line2,
|
||||
bat1_font_desc,
|
||||
bat2_font_desc,
|
||||
&battery->bat1_posy,
|
||||
&battery->bat2_posy);
|
||||
}
|
||||
|
||||
void draw_battery(void *obj, cairo_t *c)
|
||||
{
|
||||
Battery *battery = obj;
|
||||
|
||||
PangoLayout *layout = pango_cairo_create_layout(c);
|
||||
pango_layout_set_font_description(layout, bat1_font_desc);
|
||||
pango_layout_set_width(layout, battery->area.width * PANGO_SCALE);
|
||||
pango_layout_set_alignment(layout, PANGO_ALIGN_CENTER);
|
||||
pango_layout_set_wrap(layout, PANGO_WRAP_WORD_CHAR);
|
||||
pango_layout_set_ellipsize(layout, PANGO_ELLIPSIZE_NONE);
|
||||
pango_layout_set_text(layout, buf_bat_percentage, strlen(buf_bat_percentage));
|
||||
|
||||
cairo_set_source_rgba(c,
|
||||
battery->font_color.rgb[0],
|
||||
battery->font_color.rgb[1],
|
||||
battery->font_color.rgb[2],
|
||||
battery->font_color.alpha);
|
||||
|
||||
pango_cairo_update_layout(c, layout);
|
||||
draw_text(layout, c, 0, battery->bat1_posy, &battery->font_color, ((Panel *)battery->area.panel)->font_shadow);
|
||||
|
||||
pango_layout_set_font_description(layout, bat2_font_desc);
|
||||
pango_layout_set_indent(layout, 0);
|
||||
pango_layout_set_wrap(layout, PANGO_WRAP_WORD_CHAR);
|
||||
pango_layout_set_ellipsize(layout, PANGO_ELLIPSIZE_NONE);
|
||||
pango_layout_set_text(layout, buf_bat_time, strlen(buf_bat_time));
|
||||
pango_layout_set_width(layout, battery->area.width * PANGO_SCALE);
|
||||
|
||||
pango_cairo_update_layout(c, layout);
|
||||
draw_text(layout, c, 0, battery->bat2_posy, &battery->font_color, ((Panel *)battery->area.panel)->font_shadow);
|
||||
pango_cairo_show_layout(c, layout);
|
||||
|
||||
g_object_unref(layout);
|
||||
Battery *battery = (Battery *)obj;
|
||||
draw_text_area(&battery->area,
|
||||
c,
|
||||
buf_bat_line1,
|
||||
buf_bat_line2,
|
||||
bat1_font_desc,
|
||||
bat2_font_desc,
|
||||
battery->bat1_posy,
|
||||
battery->bat2_posy,
|
||||
&battery->font_color);
|
||||
}
|
||||
|
||||
void battery_dump_geometry(void *obj, int indent)
|
||||
{
|
||||
Battery *battery = obj;
|
||||
fprintf(stderr, "%*sText 1: y = %d, text = %s\n", indent, "", battery->bat1_posy, buf_bat_percentage);
|
||||
fprintf(stderr, "%*sText 2: y = %d, text = %s\n", indent, "", battery->bat2_posy, buf_bat_time);
|
||||
Battery *battery = (Battery *)obj;
|
||||
fprintf(stderr, "tint2: %*sText 1: y = %d, text = %s\n", indent, "", battery->bat1_posy, buf_bat_line1);
|
||||
fprintf(stderr, "tint2: %*sText 2: y = %d, text = %s\n", indent, "", battery->bat2_posy, buf_bat_line2);
|
||||
}
|
||||
|
||||
char *battery_get_tooltip(void *obj)
|
||||
@@ -457,5 +463,5 @@ void battery_action(void *obj, int button, int x, int y, Time time)
|
||||
command = battery_dwheel_command;
|
||||
break;
|
||||
}
|
||||
tint_exec(command, NULL, NULL, time, obj, x, y);
|
||||
tint_exec(command, NULL, NULL, time, obj, x, y, FALSE, TRUE);
|
||||
}
|
||||
|
||||
@@ -48,12 +48,15 @@ extern gboolean bat1_has_font;
|
||||
extern PangoFontDescription *bat1_font_desc;
|
||||
extern gboolean bat2_has_font;
|
||||
extern PangoFontDescription *bat2_font_desc;
|
||||
extern char *bat1_format;
|
||||
extern char *bat2_format;
|
||||
extern gboolean battery_enabled;
|
||||
extern gboolean battery_tooltip_enabled;
|
||||
extern int percentage_hide;
|
||||
|
||||
extern int8_t battery_low_status;
|
||||
extern char *battery_low_cmd;
|
||||
extern char *battery_full_cmd;
|
||||
|
||||
extern char *ac_connected_cmd;
|
||||
extern char *ac_disconnected_cmd;
|
||||
|
||||
@@ -58,7 +58,7 @@ int battery_os_update(BatteryState *state)
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
fprintf(stderr, "power update: no such sysctl");
|
||||
fprintf(stderr, "tint2: power update: no such sysctl");
|
||||
err = -1;
|
||||
}
|
||||
|
||||
|
||||
@@ -60,6 +60,20 @@ struct psy_mains {
|
||||
gboolean online;
|
||||
};
|
||||
|
||||
static gboolean is_file_non_empty(const char *path)
|
||||
{
|
||||
FILE *f = fopen(path, "r");
|
||||
if (!f)
|
||||
return FALSE;
|
||||
char buffer[1024];
|
||||
size_t count = fread(buffer, 1, sizeof(buffer), f);
|
||||
fclose(f);
|
||||
if (count > 0)
|
||||
return TRUE;
|
||||
else
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static void uevent_battery_update()
|
||||
{
|
||||
update_battery_tick(NULL);
|
||||
@@ -68,16 +82,16 @@ static struct uevent_notify psy_change = {UEVENT_CHANGE, "power_supply", NULL, u
|
||||
|
||||
static void uevent_battery_plug()
|
||||
{
|
||||
printf("reinitialize batteries after HW change\n");
|
||||
fprintf(stderr, "tint2: reinitialize batteries after HW change\n");
|
||||
reinit_battery();
|
||||
}
|
||||
static struct uevent_notify psy_plug = {UEVENT_ADD | UEVENT_REMOVE, "power_supply", NULL, uevent_battery_plug};
|
||||
|
||||
#define RETURN_ON_ERROR(err) \
|
||||
if (err) { \
|
||||
g_error_free(err); \
|
||||
fprintf(stderr, RED "%s:%d: errror" RESET "\n", __FILE__, __LINE__); \
|
||||
return FALSE; \
|
||||
#define RETURN_ON_ERROR(err) \
|
||||
if (err) { \
|
||||
g_error_free(err); \
|
||||
fprintf(stderr, RED "tint2: %s:%d: errror" RESET "\n", __FILE__, __LINE__); \
|
||||
return FALSE; \
|
||||
}
|
||||
|
||||
static GList *batteries = NULL;
|
||||
@@ -96,12 +110,13 @@ static enum psy_type power_supply_get_type(const gchar *entryname)
|
||||
gsize typelen;
|
||||
|
||||
g_file_get_contents(path_type, &type, &typelen, &error);
|
||||
g_free(path_type);
|
||||
if (error) {
|
||||
fprintf(stderr, RED "%s:%d: read failed" RESET "\n", __FILE__, __LINE__);
|
||||
fprintf(stderr, RED "tint2: %s:%d: read failed for %s" RESET "\n", __FILE__, __LINE__, path_type);
|
||||
g_free(path_type);
|
||||
g_error_free(error);
|
||||
return PSY_UNKNOWN;
|
||||
}
|
||||
g_free(path_type);
|
||||
|
||||
if (!g_strcmp0(type, "Battery\n")) {
|
||||
g_free(type);
|
||||
@@ -123,8 +138,8 @@ static gboolean init_linux_battery(struct psy_battery *bat)
|
||||
const gchar *entryname = bat->name;
|
||||
|
||||
bat->path_present = g_build_filename(battery_sys_prefix, "/sys/class/power_supply", entryname, "present", NULL);
|
||||
if (!g_file_test(bat->path_present, G_FILE_TEST_EXISTS)) {
|
||||
fprintf(stderr, RED "%s:%d: read failed" RESET "\n", __FILE__, __LINE__);
|
||||
if (!is_file_non_empty(bat->path_present)) {
|
||||
fprintf(stderr, RED "tint2: %s:%d: read failed for %s" RESET "\n", __FILE__, __LINE__, bat->path_present);
|
||||
goto err0;
|
||||
}
|
||||
|
||||
@@ -135,9 +150,8 @@ static gboolean init_linux_battery(struct psy_battery *bat)
|
||||
bat->path_rate_now = g_build_filename(battery_sys_prefix, "/sys/class/power_supply", entryname, "power_now", NULL);
|
||||
bat->unit = 'W';
|
||||
|
||||
if (!g_file_test(bat->path_level_now, G_FILE_TEST_EXISTS) ||
|
||||
!g_file_test(bat->path_level_full, G_FILE_TEST_EXISTS) ||
|
||||
!g_file_test(bat->path_rate_now, G_FILE_TEST_EXISTS)) {
|
||||
if (!is_file_non_empty(bat->path_level_now) ||
|
||||
!is_file_non_empty(bat->path_level_full)) {
|
||||
g_free(bat->path_level_now);
|
||||
g_free(bat->path_level_full);
|
||||
g_free(bat->path_rate_now);
|
||||
@@ -149,16 +163,18 @@ static gboolean init_linux_battery(struct psy_battery *bat)
|
||||
g_build_filename(battery_sys_prefix, "/sys/class/power_supply", entryname, "current_now", NULL);
|
||||
bat->unit = 'A';
|
||||
}
|
||||
if (!g_file_test(bat->path_level_now, G_FILE_TEST_EXISTS) ||
|
||||
!g_file_test(bat->path_level_full, G_FILE_TEST_EXISTS) ||
|
||||
!g_file_test(bat->path_rate_now, G_FILE_TEST_EXISTS)) {
|
||||
fprintf(stderr, RED "%s:%d: read failed" RESET "\n", __FILE__, __LINE__);
|
||||
if (!is_file_non_empty(bat->path_level_now)) {
|
||||
fprintf(stderr, RED "tint2: %s:%d: read failed for %s" RESET "\n", __FILE__, __LINE__, bat->path_level_now);
|
||||
goto err1;
|
||||
}
|
||||
if (!is_file_non_empty(bat->path_level_full)) {
|
||||
fprintf(stderr, RED "tint2: %s:%d: read failed for %s" RESET "\n", __FILE__, __LINE__, bat->path_level_full);
|
||||
goto err1;
|
||||
}
|
||||
|
||||
bat->path_status = g_build_filename(battery_sys_prefix, "/sys/class/power_supply", entryname, "status", NULL);
|
||||
if (!g_file_test(bat->path_status, G_FILE_TEST_EXISTS)) {
|
||||
fprintf(stderr, RED "%s:%d: read failed" RESET "\n", __FILE__, __LINE__);
|
||||
if (!is_file_non_empty(bat->path_status)) {
|
||||
fprintf(stderr, RED "tint2: %s:%d: read failed for %s" RESET "\n", __FILE__, __LINE__, bat->path_status);
|
||||
goto err2;
|
||||
}
|
||||
|
||||
@@ -181,8 +197,8 @@ static gboolean init_linux_mains(struct psy_mains *ac)
|
||||
const gchar *entryname = ac->name;
|
||||
|
||||
ac->path_online = g_build_filename(battery_sys_prefix, "/sys/class/power_supply", entryname, "online", NULL);
|
||||
if (!g_file_test(ac->path_online, G_FILE_TEST_EXISTS)) {
|
||||
fprintf(stderr, RED "%s:%d: read failed" RESET "\n", __FILE__, __LINE__);
|
||||
if (!is_file_non_empty(ac->path_online)) {
|
||||
fprintf(stderr, RED "tint2: %s:%d: read failed for %s" RESET "\n", __FILE__, __LINE__, ac->path_online);
|
||||
g_free(ac->path_online);
|
||||
return FALSE;
|
||||
}
|
||||
@@ -228,10 +244,10 @@ static void add_battery(const char *entryname)
|
||||
|
||||
if (init_linux_battery(bat)) {
|
||||
batteries = g_list_append(batteries, bat);
|
||||
fprintf(stdout, GREEN "Found battery \"%s\"" RESET "\n", bat->name);
|
||||
fprintf(stderr, GREEN "Found battery \"%s\"" RESET "\n", bat->name);
|
||||
} else {
|
||||
g_free(bat);
|
||||
fprintf(stderr, RED "Failed to initialize battery \"%s\"" RESET "\n", entryname);
|
||||
fprintf(stderr, RED "tint2: Failed to initialize battery \"%s\"" RESET "\n", entryname);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -242,10 +258,10 @@ static void add_mains(const char *entryname)
|
||||
|
||||
if (init_linux_mains(ac)) {
|
||||
mains = g_list_append(mains, ac);
|
||||
fprintf(stdout, GREEN "Found mains \"%s\"" RESET "\n", ac->name);
|
||||
fprintf(stderr, GREEN "Found mains \"%s\"" RESET "\n", ac->name);
|
||||
} else {
|
||||
g_free(ac);
|
||||
fprintf(stderr, RED "Failed to initialize mains \"%s\"" RESET "\n", entryname);
|
||||
fprintf(stderr, RED "tint2: Failed to initialize mains \"%s\"" RESET "\n", entryname);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -263,7 +279,7 @@ gboolean battery_os_init()
|
||||
RETURN_ON_ERROR(error);
|
||||
|
||||
while ((entryname = g_dir_read_name(directory))) {
|
||||
fprintf(stderr, GREEN "Found power device %s" RESET "\n", entryname);
|
||||
fprintf(stderr, GREEN "tint2: Found power device %s" RESET "\n", entryname);
|
||||
enum psy_type type = power_supply_get_type(entryname);
|
||||
|
||||
switch (type) {
|
||||
@@ -446,6 +462,17 @@ int battery_os_update(BatteryState *state)
|
||||
/* AC state */
|
||||
state->ac_connected = ac_connected;
|
||||
|
||||
if (state->state == BATTERY_UNKNOWN) {
|
||||
if (ac_connected) {
|
||||
if (total_rate_now == 0 && state->percentage >= 90)
|
||||
state->state = BATTERY_FULL;
|
||||
else
|
||||
state->state = BATTERY_CHARGING;
|
||||
} else {
|
||||
state->state = BATTERY_DISCHARGING;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
@@ -52,11 +52,9 @@ void destroy_button(void *obj)
|
||||
Button *button = (Button *)obj;
|
||||
if (button->frontend) {
|
||||
// This is a frontend element
|
||||
if (button->frontend->icon) {
|
||||
imlib_context_set_image(button->frontend->icon);
|
||||
imlib_free_image();
|
||||
button->frontend->icon = NULL;
|
||||
}
|
||||
free_icon(button->frontend->icon);
|
||||
free_icon(button->frontend->icon_hover);
|
||||
free_icon(button->frontend->icon_pressed);
|
||||
button->backend->instances = g_list_remove_all(button->backend->instances, button);
|
||||
free_and_null(button->frontend);
|
||||
remove_area(&button->area);
|
||||
@@ -78,8 +76,8 @@ void destroy_button(void *obj)
|
||||
free_and_null(button->backend->uwheel_command);
|
||||
|
||||
if (button->backend->instances) {
|
||||
fprintf(stderr, "Error: Attempt to destroy backend while there are still frontend instances!\n");
|
||||
exit(-1);
|
||||
fprintf(stderr, "tint2: Error: Attempt to destroy backend while there are still frontend instances!\n");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
free(button->backend);
|
||||
free(button);
|
||||
@@ -337,6 +335,7 @@ 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;
|
||||
@@ -360,34 +359,29 @@ gboolean resize_button(void *obj)
|
||||
if (button->frontend->icon_load_size != button->frontend->iconw)
|
||||
button_reload_icon(button);
|
||||
|
||||
int available_w, available_h;
|
||||
if (panel_horizontal) {
|
||||
available_w = panel->area.width;
|
||||
available_h = area->height - 2 * area->paddingy - left_right_border_width(area);
|
||||
} else {
|
||||
available_w =
|
||||
area->width - icon_w - (icon_w ? interior_padding : 0) - 2 * horiz_padding - left_right_border_width(area);
|
||||
available_h = panel->area.height;
|
||||
}
|
||||
|
||||
int txt_height_ink, txt_height, txt_width;
|
||||
if (button->backend->text) {
|
||||
if (panel_horizontal) {
|
||||
get_text_size2(button->backend->font_desc,
|
||||
&txt_height_ink,
|
||||
&txt_height,
|
||||
&txt_width,
|
||||
panel->area.height,
|
||||
panel->area.width,
|
||||
button->backend->text,
|
||||
strlen(button->backend->text),
|
||||
PANGO_WRAP_WORD_CHAR,
|
||||
PANGO_ELLIPSIZE_NONE,
|
||||
FALSE);
|
||||
} else {
|
||||
get_text_size2(button->backend->font_desc,
|
||||
&txt_height_ink,
|
||||
&txt_height,
|
||||
&txt_width,
|
||||
panel->area.height,
|
||||
button->area.width - icon_w - (icon_w ? interior_padding : 0) - 2 * horiz_padding -
|
||||
left_right_border_width(&button->area),
|
||||
button->backend->text,
|
||||
strlen(button->backend->text),
|
||||
PANGO_WRAP_WORD_CHAR,
|
||||
PANGO_ELLIPSIZE_NONE,
|
||||
FALSE);
|
||||
}
|
||||
get_text_size2(button->backend->font_desc,
|
||||
&txt_height_ink,
|
||||
&txt_height,
|
||||
&txt_width,
|
||||
available_h,
|
||||
available_w,
|
||||
button->backend->text,
|
||||
strlen(button->backend->text),
|
||||
PANGO_WRAP_WORD_CHAR,
|
||||
PANGO_ELLIPSIZE_NONE,
|
||||
FALSE);
|
||||
} else {
|
||||
txt_height_ink = txt_height = txt_width = 0;
|
||||
}
|
||||
@@ -466,7 +460,7 @@ void draw_button(void *obj, cairo_t *c)
|
||||
PangoLayout *layout = pango_cairo_create_layout(c);
|
||||
|
||||
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 + 1) * 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);
|
||||
@@ -492,7 +486,7 @@ void button_dump_geometry(void *obj, int indent)
|
||||
Imlib_Image tmp = imlib_context_get_image();
|
||||
imlib_context_set_image(button->frontend->icon);
|
||||
fprintf(stderr,
|
||||
"%*sIcon: x = %d, y = %d, w = %d, h = %d\n",
|
||||
"tint2: %*sIcon: x = %d, y = %d, w = %d, h = %d\n",
|
||||
indent,
|
||||
"",
|
||||
button->frontend->iconx,
|
||||
@@ -503,7 +497,7 @@ void button_dump_geometry(void *obj, int indent)
|
||||
imlib_context_set_image(tmp);
|
||||
}
|
||||
fprintf(stderr,
|
||||
"%*sText: x = %d, y = %d, w = %d, align = %s, text = %s\n",
|
||||
"tint2: %*sText: x = %d, y = %d, w = %d, align = %s, text = %s\n",
|
||||
indent,
|
||||
"",
|
||||
button->frontend->textx,
|
||||
@@ -534,7 +528,7 @@ void button_action(void *obj, int mouse_button, int x, int y, Time time)
|
||||
command = button->backend->dwheel_command;
|
||||
break;
|
||||
}
|
||||
tint_exec(command, NULL, NULL, time, obj, x, y);
|
||||
tint_exec(command, NULL, NULL, time, obj, x, y, FALSE, TRUE);
|
||||
}
|
||||
|
||||
char *button_get_tooltip(void *obj)
|
||||
|
||||
@@ -51,7 +51,7 @@ static char buf_time[256];
|
||||
static char buf_date[256];
|
||||
static char buf_tooltip[512];
|
||||
int clock_enabled;
|
||||
static timeout *clock_timeout;
|
||||
static Timer clock_timer;
|
||||
|
||||
void clock_init_fonts();
|
||||
char *clock_get_tooltip(void *obj);
|
||||
@@ -61,11 +61,11 @@ void clock_dump_geometry(void *obj, int indent);
|
||||
void default_clock()
|
||||
{
|
||||
clock_enabled = 0;
|
||||
clock_timeout = NULL;
|
||||
time1_format = NULL;
|
||||
time1_timezone = NULL;
|
||||
time2_format = NULL;
|
||||
time2_timezone = NULL;
|
||||
INIT_TIMER(clock_timer);
|
||||
time_tooltip_format = NULL;
|
||||
time_tooltip_timezone = NULL;
|
||||
clock_lclick_command = NULL;
|
||||
@@ -77,6 +77,9 @@ void default_clock()
|
||||
time1_font_desc = NULL;
|
||||
time2_has_font = FALSE;
|
||||
time2_font_desc = NULL;
|
||||
buf_time[0] = 0;
|
||||
buf_date[0] = 0;
|
||||
buf_tooltip[0] = 0;
|
||||
}
|
||||
|
||||
void cleanup_clock()
|
||||
@@ -107,33 +110,7 @@ void cleanup_clock()
|
||||
clock_uwheel_command = NULL;
|
||||
free(clock_dwheel_command);
|
||||
clock_dwheel_command = NULL;
|
||||
stop_timeout(clock_timeout);
|
||||
clock_timeout = NULL;
|
||||
}
|
||||
|
||||
void update_clocks_sec(void *arg)
|
||||
{
|
||||
gettimeofday(&time_clock, 0);
|
||||
if (time1_format) {
|
||||
for (int i = 0; i < num_panels; i++)
|
||||
panels[i].clock.area.resize_needed = 1;
|
||||
}
|
||||
schedule_panel_redraw();
|
||||
}
|
||||
|
||||
void update_clocks_min(void *arg)
|
||||
{
|
||||
// remember old_sec because after suspend/hibernate the clock should be updated directly, and not
|
||||
// on next minute change
|
||||
time_t old_sec = time_clock.tv_sec;
|
||||
gettimeofday(&time_clock, 0);
|
||||
if (time_clock.tv_sec % 60 == 0 || time_clock.tv_sec - old_sec > 60) {
|
||||
if (time1_format) {
|
||||
for (int i = 0; i < num_panels; i++)
|
||||
panels[i].clock.area.resize_needed = 1;
|
||||
}
|
||||
schedule_panel_redraw();
|
||||
}
|
||||
destroy_timer(&clock_timer);
|
||||
}
|
||||
|
||||
struct tm *clock_gettime_for_tz(const char *timezone)
|
||||
@@ -152,6 +129,46 @@ struct tm *clock_gettime_for_tz(const char *timezone)
|
||||
}
|
||||
}
|
||||
|
||||
void update_clocks()
|
||||
{
|
||||
if (time1_format)
|
||||
strftime(buf_time, sizeof(buf_time), time1_format, clock_gettime_for_tz(time1_timezone));
|
||||
if (time2_format)
|
||||
strftime(buf_date, sizeof(buf_date), time2_format, clock_gettime_for_tz(time2_timezone));
|
||||
if (time1_format || time2_format) {
|
||||
for (int i = 0; i < num_panels; i++)
|
||||
panels[i].clock.area.resize_needed = 1;
|
||||
}
|
||||
schedule_panel_redraw();
|
||||
}
|
||||
|
||||
int ms_until_second_change(struct timeval* tm)
|
||||
{
|
||||
long us_until_change = 1000000 - tm->tv_usec;
|
||||
// compute ms, rounding up so we don't ask to wait too short
|
||||
int ms = (us_until_change+999)/1000;
|
||||
return ms;
|
||||
}
|
||||
|
||||
void update_clocks_sec(void *arg)
|
||||
{
|
||||
gettimeofday(&time_clock, 0);
|
||||
update_clocks();
|
||||
change_timer(&clock_timer, true, ms_until_second_change(&time_clock), 0, update_clocks_sec, 0);
|
||||
}
|
||||
|
||||
void update_clocks_min(void *arg)
|
||||
{
|
||||
// remember old_sec because after suspend/hibernate the clock should be updated directly, and not
|
||||
// on next minute change
|
||||
static time_t old_sec = 0;
|
||||
gettimeofday(&time_clock, 0);
|
||||
if (time_clock.tv_sec % 60 == 0 || time_clock.tv_sec - old_sec > 60 || (time1_format && !buf_time[0]) || (time2_format && !buf_date[0]))
|
||||
update_clocks();
|
||||
old_sec = time_clock.tv_sec;
|
||||
change_timer(&clock_timer, true, ms_until_second_change(&time_clock), 0, update_clocks_min, 0);
|
||||
}
|
||||
|
||||
gboolean time_format_needs_sec_ticks(char *time_format)
|
||||
{
|
||||
if (!time_format)
|
||||
@@ -170,14 +187,6 @@ void init_clock_panel(void *p)
|
||||
Panel *panel = (Panel *)p;
|
||||
Clock *clock = &panel->clock;
|
||||
|
||||
if (!clock_timeout) {
|
||||
if (time_format_needs_sec_ticks(time1_format) || time_format_needs_sec_ticks(time2_format)) {
|
||||
clock_timeout = add_timeout(10, 1000, update_clocks_sec, 0, &clock_timeout);
|
||||
} else {
|
||||
clock_timeout = add_timeout(10, 1000, update_clocks_min, 0, &clock_timeout);
|
||||
}
|
||||
}
|
||||
|
||||
if (!clock->area.bg)
|
||||
clock->area.bg = &g_array_index(backgrounds, Background, 0);
|
||||
clock_init_fonts();
|
||||
@@ -205,6 +214,14 @@ void init_clock_panel(void *p)
|
||||
clock->area._get_tooltip_text = clock_get_tooltip;
|
||||
strftime(buf_tooltip, sizeof(buf_tooltip), time_tooltip_format, clock_gettime_for_tz(time_tooltip_timezone));
|
||||
}
|
||||
|
||||
if (!clock_timer.enabled_) {
|
||||
if (time_format_needs_sec_ticks(time1_format) || time_format_needs_sec_ticks(time2_format)) {
|
||||
update_clocks_sec(NULL);
|
||||
} else {
|
||||
update_clocks_min(NULL);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void clock_init_fonts()
|
||||
@@ -243,7 +260,7 @@ void clock_default_font_changed()
|
||||
schedule_panel_redraw();
|
||||
}
|
||||
|
||||
void clock_compute_text_geometry(Panel *panel,
|
||||
void clock_compute_text_geometry(Clock *clock,
|
||||
int *time_height_ink,
|
||||
int *time_height,
|
||||
int *time_width,
|
||||
@@ -251,139 +268,61 @@ void clock_compute_text_geometry(Panel *panel,
|
||||
int *date_height,
|
||||
int *date_width)
|
||||
{
|
||||
*date_height = *date_width = 0;
|
||||
strftime(buf_time, sizeof(buf_time), time1_format, clock_gettime_for_tz(time1_timezone));
|
||||
get_text_size2(time1_font_desc,
|
||||
time_height_ink,
|
||||
time_height,
|
||||
time_width,
|
||||
panel->area.height,
|
||||
panel->area.width,
|
||||
buf_time,
|
||||
strlen(buf_time),
|
||||
PANGO_WRAP_WORD_CHAR,
|
||||
PANGO_ELLIPSIZE_NONE,
|
||||
FALSE);
|
||||
if (time2_format) {
|
||||
strftime(buf_date, sizeof(buf_date), time2_format, clock_gettime_for_tz(time2_timezone));
|
||||
get_text_size2(time2_font_desc,
|
||||
date_height_ink,
|
||||
date_height,
|
||||
date_width,
|
||||
panel->area.height,
|
||||
panel->area.width,
|
||||
buf_date,
|
||||
strlen(buf_date),
|
||||
PANGO_WRAP_WORD_CHAR,
|
||||
PANGO_ELLIPSIZE_NONE,
|
||||
FALSE);
|
||||
}
|
||||
area_compute_text_geometry(&clock->area,
|
||||
buf_time,
|
||||
time2_format ? buf_date : NULL,
|
||||
time1_font_desc,
|
||||
time2_font_desc,
|
||||
time_height_ink,
|
||||
time_height,
|
||||
time_width,
|
||||
date_height_ink,
|
||||
date_height,
|
||||
date_width);
|
||||
}
|
||||
|
||||
int clock_compute_desired_size(void *obj)
|
||||
{
|
||||
Clock *clock = (Clock *)obj;
|
||||
Panel *panel = (Panel *)clock->area.panel;
|
||||
int time_height_ink, time_height, time_width, date_height_ink, date_height, date_width;
|
||||
clock_compute_text_geometry(panel,
|
||||
&time_height_ink,
|
||||
&time_height,
|
||||
&time_width,
|
||||
&date_height_ink,
|
||||
&date_height,
|
||||
&date_width);
|
||||
|
||||
if (panel_horizontal) {
|
||||
int new_size = (time_width > date_width) ? time_width : date_width;
|
||||
new_size += 2 * clock->area.paddingxlr + left_right_border_width(&clock->area);
|
||||
return new_size;
|
||||
} else {
|
||||
int new_size = time_height + date_height + 2 * clock->area.paddingxlr + top_bottom_border_width(&clock->area);
|
||||
return new_size;
|
||||
}
|
||||
return text_area_compute_desired_size(&clock->area,
|
||||
buf_time,
|
||||
time2_format ? buf_date : NULL,
|
||||
time1_font_desc,
|
||||
time2_font_desc);
|
||||
}
|
||||
|
||||
gboolean resize_clock(void *obj)
|
||||
{
|
||||
Clock *clock = (Clock *)obj;
|
||||
Panel *panel = (Panel *)clock->area.panel;
|
||||
gboolean result = FALSE;
|
||||
|
||||
schedule_redraw(&clock->area);
|
||||
|
||||
int time_height_ink, time_height, time_width, date_height_ink, date_height, date_width;
|
||||
clock_compute_text_geometry(panel,
|
||||
&time_height_ink,
|
||||
&time_height,
|
||||
&time_width,
|
||||
&date_height_ink,
|
||||
&date_height,
|
||||
&date_width);
|
||||
|
||||
int new_size = clock_compute_desired_size(clock);
|
||||
if (panel_horizontal) {
|
||||
if (new_size > clock->area.width || new_size < (clock->area.width - 6)) {
|
||||
// we try to limit the number of resizes
|
||||
clock->area.width = new_size + 1;
|
||||
clock->time1_posy = (clock->area.height - time_height) / 2;
|
||||
if (time2_format) {
|
||||
clock->time1_posy -= (date_height) / 2;
|
||||
clock->time2_posy = clock->time1_posy + time_height;
|
||||
}
|
||||
result = TRUE;
|
||||
}
|
||||
} else {
|
||||
if (new_size != clock->area.height) {
|
||||
// we try to limit the number of resizes
|
||||
clock->area.height = new_size;
|
||||
clock->time1_posy = (clock->area.height - time_height) / 2;
|
||||
if (time2_format) {
|
||||
clock->time1_posy -= (date_height) / 2;
|
||||
clock->time2_posy = clock->time1_posy + time_height;
|
||||
}
|
||||
result = TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
return resize_text_area(&clock->area,
|
||||
buf_time,
|
||||
time2_format ? buf_date : NULL,
|
||||
time1_font_desc,
|
||||
time2_font_desc,
|
||||
&clock->time1_posy,
|
||||
&clock->time2_posy);
|
||||
}
|
||||
|
||||
void draw_clock(void *obj, cairo_t *c)
|
||||
{
|
||||
Clock *clock = obj;
|
||||
PangoLayout *layout = pango_cairo_create_layout(c);
|
||||
|
||||
pango_layout_set_font_description(layout, time1_font_desc);
|
||||
pango_layout_set_width(layout, clock->area.width * PANGO_SCALE);
|
||||
pango_layout_set_alignment(layout, PANGO_ALIGN_CENTER);
|
||||
pango_layout_set_wrap(layout, PANGO_WRAP_WORD_CHAR);
|
||||
pango_layout_set_ellipsize(layout, PANGO_ELLIPSIZE_NONE);
|
||||
pango_layout_set_text(layout, buf_time, strlen(buf_time));
|
||||
|
||||
cairo_set_source_rgba(c, clock->font.rgb[0], clock->font.rgb[1], clock->font.rgb[2], clock->font.alpha);
|
||||
|
||||
pango_cairo_update_layout(c, layout);
|
||||
draw_text(layout, c, 0, clock->time1_posy, &clock->font, ((Panel *)clock->area.panel)->font_shadow);
|
||||
|
||||
if (time2_format) {
|
||||
pango_layout_set_font_description(layout, time2_font_desc);
|
||||
pango_layout_set_indent(layout, 0);
|
||||
pango_layout_set_text(layout, buf_date, strlen(buf_date));
|
||||
pango_layout_set_width(layout, clock->area.width * PANGO_SCALE);
|
||||
|
||||
pango_cairo_update_layout(c, layout);
|
||||
draw_text(layout, c, 0, clock->time2_posy, &clock->font, ((Panel *)clock->area.panel)->font_shadow);
|
||||
}
|
||||
|
||||
g_object_unref(layout);
|
||||
Clock *clock = (Clock *)obj;
|
||||
draw_text_area(&clock->area,
|
||||
c,
|
||||
buf_time,
|
||||
time2_format ? buf_date : NULL,
|
||||
time1_font_desc,
|
||||
time2_font_desc,
|
||||
clock->time1_posy,
|
||||
clock->time2_posy,
|
||||
&clock->font);
|
||||
}
|
||||
|
||||
void clock_dump_geometry(void *obj, int indent)
|
||||
{
|
||||
Clock *clock = (Clock *)obj;
|
||||
fprintf(stderr, "%*sText 1: y = %d, text = %s\n", indent, "", clock->time1_posy, buf_time);
|
||||
fprintf(stderr, "tint2: %*sText 1: y = %d, text = %s\n", indent, "", clock->time1_posy, buf_time);
|
||||
if (time2_format) {
|
||||
fprintf(stderr, "%*sText 2: y = %d, text = %s\n", indent, "", clock->time2_posy, buf_date);
|
||||
fprintf(stderr, "tint2: %*sText 2: y = %d, text = %s\n", indent, "", clock->time2_posy, buf_date);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -413,5 +352,5 @@ void clock_action(void *obj, int button, int x, int y, Time time)
|
||||
command = clock_dwheel_command;
|
||||
break;
|
||||
}
|
||||
tint_exec(command, NULL, NULL, time, obj, x, y);
|
||||
tint_exec(command, NULL, NULL, time, obj, x, y, FALSE, TRUE);
|
||||
}
|
||||
|
||||
158
src/config.c
158
src/config.c
@@ -119,7 +119,7 @@ void get_action(char *event, MouseAction *action)
|
||||
else if (strcmp(event, "prev_task") == 0)
|
||||
*action = PREV_TASK;
|
||||
else
|
||||
fprintf(stderr, "Error: unrecognized action '%s'. Please fix your config file.\n", event);
|
||||
fprintf(stderr, "tint2: Error: unrecognized action '%s'. Please fix your config file.\n", event);
|
||||
}
|
||||
|
||||
int get_task_status(char *status)
|
||||
@@ -135,27 +135,36 @@ int get_task_status(char *status)
|
||||
|
||||
int config_get_monitor(char *monitor)
|
||||
{
|
||||
if (strcmp(monitor, "all") != 0) {
|
||||
char *endptr;
|
||||
int ret_int = strtol(monitor, &endptr, 10);
|
||||
if (*endptr == 0)
|
||||
return ret_int - 1;
|
||||
else {
|
||||
// monitor specified by name, not by index
|
||||
int i, j;
|
||||
for (i = 0; i < server.num_monitors; ++i) {
|
||||
if (server.monitors[i].names == 0)
|
||||
// xrandr can't identify monitors
|
||||
continue;
|
||||
j = 0;
|
||||
while (server.monitors[i].names[j] != 0) {
|
||||
if (strcmp(monitor, server.monitors[i].names[j++]) == 0)
|
||||
return i;
|
||||
}
|
||||
if (strcmp(monitor, "primary") == 0) {
|
||||
for (int i = 0; i < server.num_monitors; ++i) {
|
||||
if (server.monitors[i].primary)
|
||||
return i;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
if (strcmp(monitor, "all") == 0) {
|
||||
return -1;
|
||||
}
|
||||
char *endptr;
|
||||
int ret_int = strtol(monitor, &endptr, 10);
|
||||
if (*endptr == 0)
|
||||
return ret_int - 1;
|
||||
else {
|
||||
// monitor specified by name, not by index
|
||||
int i, j;
|
||||
for (i = 0; i < server.num_monitors; ++i) {
|
||||
if (server.monitors[i].names == 0)
|
||||
// xrandr can't identify monitors
|
||||
continue;
|
||||
j = 0;
|
||||
while (server.monitors[i].names[j] != 0) {
|
||||
if (strcmp(monitor, server.monitors[i].names[j++]) == 0)
|
||||
return i;
|
||||
}
|
||||
}
|
||||
}
|
||||
// monitor == "all" or monitor not found or xrandr can't identify monitors
|
||||
|
||||
// monitor not found or xrandr can't identify monitors => all
|
||||
return -1;
|
||||
}
|
||||
|
||||
@@ -206,7 +215,7 @@ void load_launcher_app_dir(const char *path)
|
||||
Separator *get_or_create_last_separator()
|
||||
{
|
||||
if (!panel_config.separator_list) {
|
||||
fprintf(stderr, "Warning: separator items should shart with 'separator = new'\n");
|
||||
fprintf(stderr, "tint2: Warning: separator items should shart with 'separator = new'\n");
|
||||
panel_config.separator_list = g_list_append(panel_config.separator_list, create_separator());
|
||||
}
|
||||
return (Separator *)g_list_last(panel_config.separator_list)->data;
|
||||
@@ -215,7 +224,7 @@ Separator *get_or_create_last_separator()
|
||||
Execp *get_or_create_last_execp()
|
||||
{
|
||||
if (!panel_config.execp_list) {
|
||||
fprintf(stderr, "Warning: execp items should start with 'execp = new'\n");
|
||||
fprintf(stderr, "tint2: Warning: execp items should start with 'execp = new'\n");
|
||||
panel_config.execp_list = g_list_append(panel_config.execp_list, create_execp());
|
||||
}
|
||||
return (Execp *)g_list_last(panel_config.execp_list)->data;
|
||||
@@ -224,7 +233,7 @@ Execp *get_or_create_last_execp()
|
||||
Button *get_or_create_last_button()
|
||||
{
|
||||
if (!panel_config.button_list) {
|
||||
fprintf(stderr, "Warning: button items should start with 'button = new'\n");
|
||||
fprintf(stderr, "tint2: Warning: button items should start with 'button = new'\n");
|
||||
panel_config.button_list = g_list_append(panel_config.button_list, create_button());
|
||||
}
|
||||
return (Button *)g_list_last(panel_config.button_list)->data;
|
||||
@@ -341,6 +350,12 @@ void add_entry(char *key, char *value)
|
||||
id = (id < gradients->len && id >= 0) ? id : -1;
|
||||
if (id >= 0)
|
||||
bg->gradients[MOUSE_DOWN] = &g_array_index(gradients, GradientClass, id);
|
||||
} else if (strcmp(key, "border_content_tint_weight") == 0) {
|
||||
Background *bg = &g_array_index(backgrounds, Background, backgrounds->len - 1);
|
||||
bg->border_content_tint_weight = MAX(0.0, MIN(1.0, atoi(value) / 100.));
|
||||
} else if (strcmp(key, "background_content_tint_weight") == 0) {
|
||||
Background *bg = &g_array_index(backgrounds, Background, backgrounds->len - 1);
|
||||
bg->fill_content_tint_weight = MAX(0.0, MIN(1.0, atoi(value) / 100.));
|
||||
}
|
||||
|
||||
/* Gradients */
|
||||
@@ -381,8 +396,6 @@ void add_entry(char *key, char *value)
|
||||
/* Panel */
|
||||
else if (strcmp(key, "panel_monitor") == 0) {
|
||||
panel_config.monitor = config_get_monitor(value);
|
||||
} else if (strcmp(key, "primary_monitor_first") == 0) {
|
||||
primary_monitor_first = atoi(value);
|
||||
} else if (strcmp(key, "panel_shrink") == 0) {
|
||||
panel_shrink = atoi(value);
|
||||
} else if (strcmp(key, "panel_size") == 0) {
|
||||
@@ -408,6 +421,7 @@ void add_entry(char *key, char *value)
|
||||
}
|
||||
} else if (strcmp(key, "panel_items") == 0) {
|
||||
new_config_file = TRUE;
|
||||
free_and_null(panel_items_order);
|
||||
panel_items_order = strdup(value);
|
||||
systray_enabled = 0;
|
||||
launcher_enabled = 0;
|
||||
@@ -425,7 +439,7 @@ void add_entry(char *key, char *value)
|
||||
#ifdef ENABLE_BATTERY
|
||||
battery_enabled = 1;
|
||||
#else
|
||||
fprintf(stderr, "tint2 is build without battery support\n");
|
||||
fprintf(stderr, "tint2: tint2 has been compiled without battery support\n");
|
||||
#endif
|
||||
}
|
||||
if (panel_items_order[j] == 'S') {
|
||||
@@ -544,6 +558,11 @@ void add_entry(char *key, char *value)
|
||||
#ifdef ENABLE_BATTERY
|
||||
if (strlen(value) > 0)
|
||||
battery_low_cmd = strdup(value);
|
||||
#endif
|
||||
} else if (strcmp(key, "battery_full_cmd") == 0) {
|
||||
#ifdef ENABLE_BATTERY
|
||||
if (strlen(value) > 0)
|
||||
battery_full_cmd = strdup(value);
|
||||
#endif
|
||||
} else if (strcmp(key, "ac_connected_cmd") == 0) {
|
||||
#ifdef ENABLE_BATTERY
|
||||
@@ -564,6 +583,21 @@ void add_entry(char *key, char *value)
|
||||
#ifdef ENABLE_BATTERY
|
||||
bat2_font_desc = pango_font_description_from_string(value);
|
||||
bat2_has_font = TRUE;
|
||||
#endif
|
||||
} else if (strcmp(key, "bat1_format") == 0) {
|
||||
#ifdef ENABLE_BATTERY
|
||||
if (strlen(value) > 0) {
|
||||
free(bat1_format);
|
||||
bat1_format = strdup(value);
|
||||
battery_enabled = 1;
|
||||
}
|
||||
#endif
|
||||
} else if (strcmp(key, "bat2_format") == 0) {
|
||||
#ifdef ENABLE_BATTERY
|
||||
if (strlen(value) > 0) {
|
||||
free(bat2_format);
|
||||
bat2_format = strdup(value);
|
||||
}
|
||||
#endif
|
||||
} else if (strcmp(key, "battery_font_color") == 0) {
|
||||
#ifdef ENABLE_BATTERY
|
||||
@@ -626,7 +660,7 @@ void add_entry(char *key, char *value)
|
||||
else if (g_str_equal(value, "dots"))
|
||||
separator->style = SEPARATOR_DOTS;
|
||||
else
|
||||
fprintf(stderr, RED "Invalid separator_style value: %s" RESET "\n", value);
|
||||
fprintf(stderr, RED "tint2: Invalid separator_style value: %s" RESET "\n", value);
|
||||
} else if (strcmp(key, "separator_size") == 0) {
|
||||
Separator *separator = get_or_create_last_separator();
|
||||
separator->thickness = atoi(value);
|
||||
@@ -652,8 +686,8 @@ void add_entry(char *key, char *value)
|
||||
Execp *execp = get_or_create_last_execp();
|
||||
execp->backend->interval = 0;
|
||||
int v = atoi(value);
|
||||
if (v < 1) {
|
||||
fprintf(stderr, "execp_interval must be an integer >= 1\n");
|
||||
if (v < 0) {
|
||||
fprintf(stderr, "tint2: execp_interval must be an integer >= 0\n");
|
||||
} else {
|
||||
execp->backend->interval = v;
|
||||
}
|
||||
@@ -673,6 +707,7 @@ void add_entry(char *key, char *value)
|
||||
Execp *execp = get_or_create_last_execp();
|
||||
free_and_null(execp->backend->tooltip);
|
||||
execp->backend->tooltip = strdup(value);
|
||||
execp->backend->has_user_tooltip = TRUE;
|
||||
} else if (strcmp(key, "execp_font") == 0) {
|
||||
Execp *execp = get_or_create_last_execp();
|
||||
pango_font_description_free(execp->backend->font_desc);
|
||||
@@ -708,7 +743,7 @@ void add_entry(char *key, char *value)
|
||||
Execp *execp = get_or_create_last_execp();
|
||||
int v = atoi(value);
|
||||
if (v < 0) {
|
||||
fprintf(stderr, "execp_icon_w must be an integer >= 0\n");
|
||||
fprintf(stderr, "tint2: execp_icon_w must be an integer >= 0\n");
|
||||
} else {
|
||||
execp->backend->icon_w = v;
|
||||
}
|
||||
@@ -716,7 +751,7 @@ void add_entry(char *key, char *value)
|
||||
Execp *execp = get_or_create_last_execp();
|
||||
int v = atoi(value);
|
||||
if (v < 0) {
|
||||
fprintf(stderr, "execp_icon_h must be an integer >= 0\n");
|
||||
fprintf(stderr, "tint2: execp_icon_h must be an integer >= 0\n");
|
||||
} else {
|
||||
execp->backend->icon_h = v;
|
||||
}
|
||||
@@ -750,17 +785,23 @@ void add_entry(char *key, char *value)
|
||||
/* Button */
|
||||
else if (strcmp(key, "button") == 0) {
|
||||
panel_config.button_list = g_list_append(panel_config.button_list, create_button());
|
||||
} else if (strcmp(key, "button_icon") == 0 && strlen(value)) {
|
||||
Button *button = get_or_create_last_button();
|
||||
button->backend->icon_name = strdup(value);
|
||||
} else if (strcmp(key, "button_text") == 0 && strlen(value)) {
|
||||
Button *button = get_or_create_last_button();
|
||||
free_and_null(button->backend->text);
|
||||
button->backend->text = strdup(value);
|
||||
} else if (strcmp(key, "button_tooltip") == 0 && strlen(value)) {
|
||||
Button *button = get_or_create_last_button();
|
||||
free_and_null(button->backend->tooltip);
|
||||
button->backend->tooltip = strdup(value);
|
||||
} else if (strcmp(key, "button_icon") == 0) {
|
||||
if (strlen(value)) {
|
||||
Button *button = get_or_create_last_button();
|
||||
button->backend->icon_name = strdup(value);
|
||||
}
|
||||
} else if (strcmp(key, "button_text") == 0) {
|
||||
if (strlen(value)) {
|
||||
Button *button = get_or_create_last_button();
|
||||
free_and_null(button->backend->text);
|
||||
button->backend->text = strdup(value);
|
||||
}
|
||||
} else if (strcmp(key, "button_tooltip") == 0) {
|
||||
if (strlen(value)) {
|
||||
Button *button = get_or_create_last_button();
|
||||
free_and_null(button->backend->tooltip);
|
||||
button->backend->tooltip = strdup(value);
|
||||
}
|
||||
} else if (strcmp(key, "button_font") == 0) {
|
||||
Button *button = get_or_create_last_button();
|
||||
pango_font_description_free(button->backend->font_desc);
|
||||
@@ -960,6 +1001,8 @@ void add_entry(char *key, char *value)
|
||||
hide_inactive_tasks = atoi(value);
|
||||
} else if (strcmp(key, "taskbar_hide_different_monitor") == 0) {
|
||||
hide_task_diff_monitor = atoi(value);
|
||||
} else if (strcmp(key, "taskbar_hide_different_desktop") == 0) {
|
||||
hide_task_diff_desktop = atoi(value);
|
||||
} else if (strcmp(key, "taskbar_hide_if_empty") == 0) {
|
||||
hide_taskbar_if_empty = atoi(value);
|
||||
} else if (strcmp(key, "taskbar_always_show_all_desktop_tasks") == 0) {
|
||||
@@ -1049,11 +1092,18 @@ void add_entry(char *key, char *value)
|
||||
panel_config.g_task.config_background_mask |= (1 << status);
|
||||
if (status == TASK_NORMAL)
|
||||
panel_config.g_task.area.bg = panel_config.g_task.background[TASK_NORMAL];
|
||||
if (panel_config.g_task.background[status]->border_content_tint_weight > 0 ||
|
||||
panel_config.g_task.background[status]->fill_content_tint_weight > 0)
|
||||
panel_config.g_task.has_content_tint = TRUE;
|
||||
}
|
||||
}
|
||||
// "tooltip" is deprecated but here for backwards compatibility
|
||||
else if (strcmp(key, "task_tooltip") == 0 || strcmp(key, "tooltip") == 0)
|
||||
panel_config.g_task.tooltip_enabled = atoi(value);
|
||||
else if (strcmp(key, "task_thumbnail") == 0)
|
||||
panel_config.g_task.thumbnail_enabled = atoi(value);
|
||||
else if (strcmp(key, "task_thumbnail_size") == 0)
|
||||
panel_config.g_task.thumbnail_width = MAX(8, atoi(value));
|
||||
|
||||
/* Systray */
|
||||
else if (strcmp(key, "systray_padding") == 0) {
|
||||
@@ -1094,10 +1144,13 @@ void add_entry(char *key, char *value)
|
||||
systray.saturation = atoi(value2);
|
||||
systray.brightness = atoi(value3);
|
||||
} else if (strcmp(key, "systray_monitor") == 0) {
|
||||
systray_monitor = atoi(value) - 1;
|
||||
systray_monitor = MAX(0, config_get_monitor(value));
|
||||
} else if (strcmp(key, "systray_name_filter") == 0) {
|
||||
if (systray_hide_name_filter)
|
||||
if (systray_hide_name_filter) {
|
||||
fprintf(stderr, "tint2: Error: duplicate option 'systray_name_filter'. Please use it only once. See "
|
||||
"https://gitlab.com/o9000/tint2/issues/652\n");
|
||||
free(systray_hide_name_filter);
|
||||
}
|
||||
systray_hide_name_filter = strdup(value);
|
||||
}
|
||||
|
||||
@@ -1250,8 +1303,13 @@ void add_entry(char *key, char *value)
|
||||
}
|
||||
}
|
||||
#endif
|
||||
else
|
||||
fprintf(stderr, "tint2 : invalid option \"%s\",\n upgrade tint2 or correct your config file\n", key);
|
||||
else if (strcmp(key, "primary_monitor_first") == 0) {
|
||||
fprintf(stderr,
|
||||
"tint2: deprecated config option \"%s\"\n"
|
||||
" Please see the documentation regarding the alternatives.\n",
|
||||
key);
|
||||
} else
|
||||
fprintf(stderr, "tint2: invalid option \"%s\",\n upgrade tint2 or correct your config file\n", key);
|
||||
|
||||
if (value1)
|
||||
free(value1);
|
||||
@@ -1263,6 +1321,8 @@ void add_entry(char *key, char *value)
|
||||
|
||||
gboolean config_read_file(const char *path)
|
||||
{
|
||||
fprintf(stderr, "tint2: Loading config file: %s\n", path);
|
||||
|
||||
FILE *fp = fopen(path, "r");
|
||||
if (!fp)
|
||||
return FALSE;
|
||||
@@ -1331,12 +1391,12 @@ gboolean config_read_default_path()
|
||||
|
||||
// copy tint2rc from system directory to user directory
|
||||
|
||||
fprintf(stderr, "tint2 warning: could not find a config file! Creating a default one.\n");
|
||||
fprintf(stderr, "tint2: could not find a config file! Creating a default one.\n");
|
||||
// According to the XDG Base Directory Specification
|
||||
// (https://specifications.freedesktop.org/basedir-spec/basedir-spec-0.6.html)
|
||||
// if the user's config directory does not exist, we should create it with permissions set to 0700.
|
||||
if (!g_file_test(g_get_user_config_dir(), G_FILE_TEST_IS_DIR))
|
||||
g_mkdir(g_get_user_config_dir(), 0700);
|
||||
g_mkdir_with_parents(g_get_user_config_dir(), 0700);
|
||||
|
||||
gchar *path2 = 0;
|
||||
system_dirs = g_get_system_config_dirs();
|
||||
@@ -1353,7 +1413,7 @@ gboolean config_read_default_path()
|
||||
// copy file in user directory (path1)
|
||||
gchar *dir = g_build_filename(g_get_user_config_dir(), "tint2", NULL);
|
||||
if (!g_file_test(dir, G_FILE_TEST_IS_DIR))
|
||||
g_mkdir(dir, 0700);
|
||||
g_mkdir_with_parents(dir, 0700);
|
||||
g_free(dir);
|
||||
|
||||
path1 = g_build_filename(g_get_user_config_dir(), "tint2", "tint2rc", NULL);
|
||||
@@ -1369,7 +1429,7 @@ gboolean config_read_default_path()
|
||||
// generate config file
|
||||
gchar *dir = g_build_filename(g_get_user_config_dir(), "tint2", NULL);
|
||||
if (!g_file_test(dir, G_FILE_TEST_IS_DIR))
|
||||
g_mkdir(dir, 0700);
|
||||
g_mkdir_with_parents(dir, 0700);
|
||||
g_free(dir);
|
||||
|
||||
path1 = g_build_filename(g_get_user_config_dir(), "tint2", "tint2rc", NULL);
|
||||
|
||||
5
src/default_icon.c
Normal file
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
10
src/default_icon.h
Normal file
@@ -0,0 +1,10 @@
|
||||
#ifndef default_icon_h
|
||||
#define default_icon_h
|
||||
|
||||
#include <Imlib2.h>
|
||||
|
||||
extern int default_icon_width;
|
||||
extern int default_icon_height;
|
||||
extern DATA32 default_icon_data[];
|
||||
|
||||
#endif
|
||||
444
src/drag_and_drop.c
Normal file
444
src/drag_and_drop.c
Normal file
@@ -0,0 +1,444 @@
|
||||
/**************************************************************************
|
||||
* Copyright (C) 2017 tint2 authors
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License version 2
|
||||
* as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
**************************************************************************/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "drag_and_drop.h"
|
||||
#include "panel.h"
|
||||
#include "server.h"
|
||||
#include "task.h"
|
||||
|
||||
// Drag and Drop state variables
|
||||
static Window dnd_source_window;
|
||||
static Window dnd_target_window;
|
||||
static int dnd_version;
|
||||
static Atom dnd_selection;
|
||||
static Atom dnd_atom;
|
||||
static int dnd_sent_request;
|
||||
static LauncherIcon *dnd_launcher_icon;
|
||||
gboolean debug_dnd = FALSE;
|
||||
|
||||
gboolean hidden_panel_shown_for_dnd;
|
||||
|
||||
// This fetches all the data from a property
|
||||
struct Property dnd_read_property(Display *disp, Window w, Atom property)
|
||||
{
|
||||
Atom actual_type;
|
||||
int actual_format;
|
||||
unsigned long nitems;
|
||||
unsigned long bytes_after;
|
||||
unsigned char *ret = 0;
|
||||
|
||||
int read_bytes = 1024;
|
||||
|
||||
// Keep trying to read the property until there are no
|
||||
// bytes unread.
|
||||
do {
|
||||
if (ret != 0)
|
||||
XFree(ret);
|
||||
XGetWindowProperty(disp,
|
||||
w,
|
||||
property,
|
||||
0,
|
||||
read_bytes,
|
||||
False,
|
||||
AnyPropertyType,
|
||||
&actual_type,
|
||||
&actual_format,
|
||||
&nitems,
|
||||
&bytes_after,
|
||||
&ret);
|
||||
read_bytes *= 2;
|
||||
} while (bytes_after != 0);
|
||||
|
||||
if (debug_dnd)
|
||||
fprintf(stderr, "tint2: DnD %s:%d: Property:\n", __FILE__, __LINE__);
|
||||
fprintf(stderr, "tint2: DnD %s:%d: Actual type: %s\n", __FILE__, __LINE__, GetAtomName(disp, actual_type));
|
||||
fprintf(stderr, "tint2: DnD %s:%d: Actual format: %d\n", __FILE__, __LINE__, actual_format);
|
||||
fprintf(stderr, "tint2: DnD %s:%d: Number of items: %lu\n", __FILE__, __LINE__, nitems);
|
||||
|
||||
Property p;
|
||||
p.data = ret;
|
||||
p.format = actual_format;
|
||||
p.nitems = nitems;
|
||||
p.type = actual_type;
|
||||
|
||||
return p;
|
||||
}
|
||||
|
||||
// This function takes a list of targets which can be converted to (atom_list, nitems)
|
||||
// and a list of acceptable targets with prioritees (datatypes). It returns the highest
|
||||
// entry in datatypes which is also in atom_list: ie it finds the best match.
|
||||
Atom dnd_pick_target_from_list(Display *disp, Atom *atom_list, int nitems)
|
||||
{
|
||||
Atom to_be_requested = None;
|
||||
int i;
|
||||
for (i = 0; i < nitems; i++) {
|
||||
const char *atom_name = GetAtomName(disp, atom_list[i]);
|
||||
fprintf(stderr, "tint2: DnD %s:%d: Type %d = %s\n", __FILE__, __LINE__, i, atom_name);
|
||||
|
||||
// See if this data type is allowed and of higher priority (closer to zero)
|
||||
// than the present one.
|
||||
if (strcasecmp(atom_name, "STRING") == 0) {
|
||||
to_be_requested = atom_list[i];
|
||||
} else if (strcasecmp(atom_name, "text/uri-list") == 0 && !to_be_requested) {
|
||||
to_be_requested = atom_list[i];
|
||||
}
|
||||
}
|
||||
|
||||
fprintf(stderr,
|
||||
"tint2: DnD %s:%d: Accepting: Type %s\n",
|
||||
__FILE__,
|
||||
__LINE__,
|
||||
GetAtomName(server.display, to_be_requested));
|
||||
|
||||
return to_be_requested;
|
||||
}
|
||||
|
||||
// Finds the best target given up to three atoms provided (any can be None).
|
||||
// Useful for part of the Xdnd protocol.
|
||||
Atom dnd_pick_target_from_atoms(Display *disp, Atom t1, Atom t2, Atom t3)
|
||||
{
|
||||
Atom atoms[3];
|
||||
int n = 0;
|
||||
|
||||
if (t1 != None)
|
||||
atoms[n++] = t1;
|
||||
|
||||
if (t2 != None)
|
||||
atoms[n++] = t2;
|
||||
|
||||
if (t3 != None)
|
||||
atoms[n++] = t3;
|
||||
|
||||
return dnd_pick_target_from_list(disp, atoms, n);
|
||||
}
|
||||
|
||||
// Finds the best target given a local copy of a property.
|
||||
Atom dnd_pick_target_from_targets(Display *disp, Property p)
|
||||
{
|
||||
// The list of targets is a list of atoms, so it should have type XA_ATOM
|
||||
// but it may have the type TARGETS instead.
|
||||
|
||||
if ((p.type != XA_ATOM && p.type != server.atom.TARGETS) || p.format != 32) {
|
||||
// This would be really broken. Targets have to be an atom list
|
||||
// and applications should support this. Nevertheless, some
|
||||
// seem broken (MATLAB 7, for instance), so ask for STRING
|
||||
// next instead as the lowest common denominator
|
||||
return XA_STRING;
|
||||
} else {
|
||||
Atom *atom_list = (Atom *)p.data;
|
||||
|
||||
return dnd_pick_target_from_list(disp, atom_list, p.nitems);
|
||||
}
|
||||
}
|
||||
|
||||
void dnd_init()
|
||||
{
|
||||
dnd_source_window = 0;
|
||||
dnd_target_window = 0;
|
||||
dnd_version = 0;
|
||||
dnd_selection = XInternAtom(server.display, "PRIMARY", 0);
|
||||
dnd_atom = None;
|
||||
dnd_sent_request = 0;
|
||||
dnd_launcher_icon = NULL;
|
||||
hidden_panel_shown_for_dnd = FALSE;
|
||||
}
|
||||
|
||||
void handle_dnd_enter(XClientMessageEvent *e)
|
||||
{
|
||||
dnd_atom = None;
|
||||
int more_than_3 = e->data.l[1] & 1;
|
||||
dnd_source_window = e->data.l[0];
|
||||
dnd_version = (e->data.l[1] >> 24);
|
||||
|
||||
if (debug_dnd) {
|
||||
fprintf(stderr, "tint2: DnD %s:%d: DnDEnter\n", __FILE__, __LINE__);
|
||||
fprintf(stderr,
|
||||
"DnD %s:%d: DnDEnter. Supports > 3 types = %s\n",
|
||||
__FILE__,
|
||||
__LINE__,
|
||||
more_than_3 ? "yes" : "no");
|
||||
fprintf(stderr, "tint2: DnD %s:%d: Protocol version = %d\n", __FILE__, __LINE__, dnd_version);
|
||||
fprintf(stderr,
|
||||
"tint2: DnD %s:%d: Type 1 = %s\n",
|
||||
__FILE__,
|
||||
__LINE__,
|
||||
GetAtomName(server.display, e->data.l[2]));
|
||||
fprintf(stderr,
|
||||
"tint2: DnD %s:%d: Type 2 = %s\n",
|
||||
__FILE__,
|
||||
__LINE__,
|
||||
GetAtomName(server.display, e->data.l[3]));
|
||||
fprintf(stderr,
|
||||
"tint2: DnD %s:%d: Type 3 = %s\n",
|
||||
__FILE__,
|
||||
__LINE__,
|
||||
GetAtomName(server.display, e->data.l[4]));
|
||||
}
|
||||
|
||||
// Query which conversions are available and pick the best
|
||||
if (more_than_3) {
|
||||
// Fetch the list of possible conversions
|
||||
// Notice the similarity to TARGETS with paste.
|
||||
Property p = dnd_read_property(server.display, dnd_source_window, server.atom.XdndTypeList);
|
||||
dnd_atom = dnd_pick_target_from_targets(server.display, p);
|
||||
XFree(p.data);
|
||||
} else {
|
||||
// Use the available list
|
||||
dnd_atom = dnd_pick_target_from_atoms(server.display, e->data.l[2], e->data.l[3], e->data.l[4]);
|
||||
}
|
||||
|
||||
if (debug_dnd)
|
||||
fprintf(stderr,
|
||||
"tint2: DnD %s:%d: Requested type = %s\n",
|
||||
__FILE__,
|
||||
__LINE__,
|
||||
GetAtomName(server.display, dnd_atom));
|
||||
}
|
||||
|
||||
void handle_dnd_position(XClientMessageEvent *e)
|
||||
{
|
||||
dnd_target_window = e->window;
|
||||
int accept = 0;
|
||||
Panel *panel = get_panel(e->window);
|
||||
int x, y, mapX, mapY;
|
||||
Window child;
|
||||
x = (e->data.l[2] >> 16) & 0xFFFF;
|
||||
y = e->data.l[2] & 0xFFFF;
|
||||
XTranslateCoordinates(server.display, server.root_win, e->window, x, y, &mapX, &mapY, &child);
|
||||
Task *task = click_task(panel, mapX, mapY);
|
||||
if (task) {
|
||||
if (task->desktop != server.desktop)
|
||||
change_desktop(task->desktop);
|
||||
task_handle_mouse_event(task, TOGGLE);
|
||||
} else {
|
||||
LauncherIcon *icon = click_launcher_icon(panel, mapX, mapY);
|
||||
if (icon) {
|
||||
accept = 1;
|
||||
dnd_launcher_icon = icon;
|
||||
} else {
|
||||
dnd_launcher_icon = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
// send XdndStatus event to get more XdndPosition events
|
||||
XClientMessageEvent se;
|
||||
se.type = ClientMessage;
|
||||
se.window = e->data.l[0];
|
||||
se.message_type = server.atom.XdndStatus;
|
||||
se.format = 32;
|
||||
se.data.l[0] = e->window; // XID of the target window
|
||||
se.data.l[1] = accept ? 1 : 0; // bit 0: accept drop bit 1: send XdndPosition events if inside rectangle
|
||||
se.data.l[2] = 0; // Rectangle x,y for which no more XdndPosition events
|
||||
se.data.l[3] = (1 << 16) | 1; // Rectangle w,h for which no more XdndPosition events
|
||||
if (accept) {
|
||||
se.data.l[4] = server.atom.XdndActionCopy;
|
||||
} else {
|
||||
se.data.l[4] = None; // None = drop will not be accepted
|
||||
}
|
||||
|
||||
if (debug_dnd)
|
||||
fprintf(stderr,
|
||||
"tint2: DnD %s:%d: Accepted: %s\n",
|
||||
__FILE__,
|
||||
__LINE__,
|
||||
accept ? GetAtomName(server.display, (Atom)se.data.l[4]) : "no");
|
||||
|
||||
XSendEvent(server.display, e->data.l[0], False, NoEventMask, (XEvent *)&se);
|
||||
}
|
||||
|
||||
void handle_dnd_drop(XClientMessageEvent *e)
|
||||
{
|
||||
if (dnd_target_window && dnd_launcher_icon) {
|
||||
if (dnd_version >= 1) {
|
||||
XConvertSelection(server.display,
|
||||
server.atom.XdndSelection,
|
||||
dnd_atom,
|
||||
dnd_selection,
|
||||
dnd_target_window,
|
||||
e->data.l[2]);
|
||||
} else {
|
||||
XConvertSelection(server.display,
|
||||
server.atom.XdndSelection,
|
||||
dnd_atom,
|
||||
dnd_selection,
|
||||
dnd_target_window,
|
||||
CurrentTime);
|
||||
}
|
||||
} else {
|
||||
// The source is sending anyway, despite instructions to the contrary.
|
||||
// So reply that we're not interested.
|
||||
XClientMessageEvent m;
|
||||
memset(&m, 0, sizeof(m));
|
||||
m.type = ClientMessage;
|
||||
m.display = e->display;
|
||||
m.window = e->data.l[0];
|
||||
m.message_type = server.atom.XdndFinished;
|
||||
m.format = 32;
|
||||
m.data.l[0] = dnd_target_window;
|
||||
m.data.l[1] = 0;
|
||||
m.data.l[2] = None; // Failed.
|
||||
XSendEvent(server.display, e->data.l[0], False, NoEventMask, (XEvent *)&m);
|
||||
}
|
||||
}
|
||||
|
||||
void handle_dnd_selection_notify(XSelectionEvent *e)
|
||||
{
|
||||
Atom target = e->target;
|
||||
|
||||
if (debug_dnd) {
|
||||
fprintf(stderr, "tint2: DnD %s:%d: A selection notify has arrived!\n", __FILE__, __LINE__);
|
||||
fprintf(stderr,
|
||||
"DnD %s:%d: Selection atom = %s\n",
|
||||
__FILE__,
|
||||
__LINE__,
|
||||
GetAtomName(server.display, e->selection));
|
||||
fprintf(stderr, "tint2: DnD %s:%d: Target atom = %s\n", __FILE__, __LINE__, GetAtomName(server.display, target));
|
||||
fprintf(stderr,
|
||||
"DnD %s:%d: Property atom = %s\n",
|
||||
__FILE__,
|
||||
__LINE__,
|
||||
GetAtomName(server.display, e->property));
|
||||
}
|
||||
|
||||
if (dnd_launcher_icon) {
|
||||
Property prop = dnd_read_property(server.display, dnd_target_window, dnd_selection);
|
||||
|
||||
if (prop.data) {
|
||||
// If we're being given a list of targets (possible conversions)
|
||||
if (target == server.atom.TARGETS && !dnd_sent_request) {
|
||||
dnd_sent_request = 1;
|
||||
dnd_atom = dnd_pick_target_from_targets(server.display, prop);
|
||||
|
||||
if (dnd_atom == None) {
|
||||
if (debug_dnd)
|
||||
fprintf(stderr, "tint2: No matching datatypes.\n");
|
||||
} else {
|
||||
// Request the data type we are able to select
|
||||
if (debug_dnd)
|
||||
fprintf(stderr, "tint2: Now requsting type %s", GetAtomName(server.display, dnd_atom));
|
||||
XConvertSelection(server.display,
|
||||
dnd_selection,
|
||||
dnd_atom,
|
||||
dnd_selection,
|
||||
dnd_target_window,
|
||||
CurrentTime);
|
||||
}
|
||||
} else if (target == dnd_atom) {
|
||||
// Dump the binary data
|
||||
if (debug_dnd) {
|
||||
fprintf(stderr, "tint2: DnD %s:%d: Received data:\n", __FILE__, __LINE__);
|
||||
fprintf(stderr, "tint2: --------\n");
|
||||
for (int i = 0; i < prop.nitems * prop.format / 8; i++)
|
||||
fprintf(stderr, "%c", ((char *)prop.data)[i]);
|
||||
fprintf(stderr, "tint2: --------\n");
|
||||
}
|
||||
|
||||
// https://standards.freedesktop.org/desktop-entry-spec/desktop-entry-spec-latest.html#exec-variables
|
||||
GString *cmd = g_string_new(dnd_launcher_icon->cmd);
|
||||
|
||||
const char *atom_name = GetAtomName(server.display, prop.type);
|
||||
if (strcasecmp(atom_name, "STRING") == 0 || strcasecmp(atom_name, "text/uri-list") == 0) {
|
||||
GString *url = g_string_new("");
|
||||
GString *prev_url = g_string_new("");
|
||||
gboolean must_unescape = strcasecmp(atom_name, "text/uri-list") == 0;
|
||||
for (int i = 0; i < prop.nitems * prop.format / 8; i++) {
|
||||
char c = ((char *)prop.data)[i];
|
||||
if (c == '\n') {
|
||||
if (must_unescape) {
|
||||
char *raw = g_uri_unescape_string(url->str, NULL);
|
||||
if (raw) {
|
||||
g_string_assign(url, raw);
|
||||
}
|
||||
free(raw);
|
||||
}
|
||||
// Many programs cannot handle this prefix
|
||||
tint2_g_string_replace(url, "file://", "");
|
||||
// Some programs put duplicates in the list, we remove them
|
||||
if (strcmp(url->str, prev_url->str) != 0) {
|
||||
if (strstr(cmd->str, "%F")) {
|
||||
GString *piece = g_string_new("");
|
||||
g_string_append(piece, " \"");
|
||||
g_string_append(piece, url->str);
|
||||
g_string_append(piece, "\"");
|
||||
g_string_append(piece, " %F");
|
||||
tint2_g_string_replace(cmd, "%F", piece->str);
|
||||
g_string_free(piece, TRUE);
|
||||
} else if (strstr(cmd->str, "%f")) {
|
||||
GString *piece = g_string_new("");
|
||||
g_string_append(piece, " \"");
|
||||
g_string_append(piece, url->str);
|
||||
g_string_append(piece, "\"");
|
||||
tint2_g_string_replace(cmd, "%f", piece->str);
|
||||
g_string_free(piece, TRUE);
|
||||
break;
|
||||
} else {
|
||||
g_string_append(cmd, " \"");
|
||||
g_string_append(cmd, url->str);
|
||||
g_string_append(cmd, "\"");
|
||||
}
|
||||
}
|
||||
g_string_assign(prev_url, url->str);
|
||||
g_string_assign(url, "");
|
||||
} else if (c == '\r') {
|
||||
// Nothing to do
|
||||
} else {
|
||||
if (c == '`' || c == '$' || c == '\\') {
|
||||
g_string_append(url, "\\");
|
||||
}
|
||||
g_string_append_c(url, c);
|
||||
}
|
||||
}
|
||||
g_string_free(url, TRUE);
|
||||
g_string_free(prev_url, TRUE);
|
||||
}
|
||||
tint2_g_string_replace(cmd, "%F", "");
|
||||
tint2_g_string_replace(cmd, "%f", "");
|
||||
if (debug_dnd)
|
||||
fprintf(stderr, "tint2: DnD %s:%d: Running command: %s\n", __FILE__, __LINE__, cmd->str);
|
||||
tint_exec(cmd->str,
|
||||
NULL,
|
||||
NULL,
|
||||
e->time,
|
||||
NULL,
|
||||
0,
|
||||
0,
|
||||
dnd_launcher_icon->start_in_terminal,
|
||||
dnd_launcher_icon->startup_notification);
|
||||
g_string_free(cmd, TRUE);
|
||||
|
||||
// Reply OK.
|
||||
XClientMessageEvent m;
|
||||
memset(&m, 0, sizeof(m));
|
||||
m.type = ClientMessage;
|
||||
m.display = server.display;
|
||||
m.window = dnd_source_window;
|
||||
m.message_type = server.atom.XdndFinished;
|
||||
m.format = 32;
|
||||
m.data.l[0] = dnd_target_window;
|
||||
m.data.l[1] = 1;
|
||||
m.data.l[2] = server.atom.XdndActionCopy; // We only ever copy.
|
||||
XSendEvent(server.display, dnd_source_window, False, NoEventMask, (XEvent *)&m);
|
||||
XSync(server.display, False);
|
||||
}
|
||||
|
||||
XFree(prop.data);
|
||||
}
|
||||
}
|
||||
}
|
||||
22
src/drag_and_drop.h
Normal file
22
src/drag_and_drop.h
Normal file
@@ -0,0 +1,22 @@
|
||||
/**************************************************************************
|
||||
* Copyright (C) 2017 tint2 authors
|
||||
*
|
||||
**************************************************************************/
|
||||
|
||||
#ifndef DRAG_AND_DROP_H
|
||||
#define DRAG_AND_DROP_H
|
||||
|
||||
#include <X11/Xlib.h>
|
||||
#include <glib.h>
|
||||
|
||||
extern gboolean hidden_panel_shown_for_dnd;
|
||||
extern gboolean debug_dnd;
|
||||
|
||||
void dnd_init();
|
||||
|
||||
void handle_dnd_enter(XClientMessageEvent *e);
|
||||
void handle_dnd_position(XClientMessageEvent *e);
|
||||
void handle_dnd_drop(XClientMessageEvent *e);
|
||||
void handle_dnd_selection_notify(XSelectionEvent *e);
|
||||
|
||||
#endif
|
||||
@@ -18,6 +18,8 @@
|
||||
#include "timer.h"
|
||||
#include "common.h"
|
||||
|
||||
#define MAX_TOOLTIP_LEN 4096
|
||||
|
||||
void execp_timer_callback(void *arg);
|
||||
char *execp_get_tooltip(void *obj);
|
||||
void execp_init_fonts();
|
||||
@@ -30,14 +32,16 @@ void default_execp()
|
||||
|
||||
Execp *create_execp()
|
||||
{
|
||||
Execp *execp = calloc(1, sizeof(Execp));
|
||||
execp->backend = calloc(1, sizeof(ExecpBackend));
|
||||
execp->backend->child_pipe = -1;
|
||||
Execp *execp = (Execp *)calloc(1, sizeof(Execp));
|
||||
execp->backend = (ExecpBackend *)calloc(1, sizeof(ExecpBackend));
|
||||
execp->backend->child_pipe_stdout = -1;
|
||||
execp->backend->child_pipe_stderr = -1;
|
||||
execp->backend->cmd_pids = g_tree_new(cmp_ptr);
|
||||
execp->backend->interval = 30;
|
||||
execp->backend->cache_icon = TRUE;
|
||||
execp->backend->centered = TRUE;
|
||||
execp->backend->font_color.alpha = 0.5;
|
||||
INIT_TIMER(execp->backend->timer);
|
||||
return execp;
|
||||
}
|
||||
|
||||
@@ -45,10 +49,10 @@ gpointer create_execp_frontend(gconstpointer arg, gpointer data)
|
||||
{
|
||||
Execp *execp_backend = (Execp *)arg;
|
||||
|
||||
Execp *execp_frontend = calloc(1, sizeof(Execp));
|
||||
Execp *execp_frontend = (Execp *)calloc(1, sizeof(Execp));
|
||||
execp_frontend->backend = execp_backend->backend;
|
||||
execp_backend->backend->instances = g_list_append(execp_backend->backend->instances, execp_frontend);
|
||||
execp_frontend->frontend = calloc(1, sizeof(ExecpFrontend));
|
||||
execp_frontend->frontend = (ExecpFrontend *)calloc(1, sizeof(ExecpFrontend));
|
||||
return execp_frontend;
|
||||
}
|
||||
|
||||
@@ -64,24 +68,24 @@ void destroy_execp(void *obj)
|
||||
free_and_null(execp);
|
||||
} else {
|
||||
// This is a backend element
|
||||
stop_timeout(execp->backend->timer);
|
||||
execp->backend->timer = NULL;
|
||||
destroy_timer(&execp->backend->timer);
|
||||
|
||||
if (execp->backend->icon) {
|
||||
imlib_context_set_image(execp->backend->icon);
|
||||
imlib_free_image();
|
||||
execp->backend->icon = NULL;
|
||||
}
|
||||
free_and_null(execp->backend->buf_output);
|
||||
free_icon(execp->backend->icon);
|
||||
free_and_null(execp->backend->buf_stdout);
|
||||
free_and_null(execp->backend->buf_stderr);
|
||||
free_and_null(execp->backend->text);
|
||||
free_and_null(execp->backend->icon_path);
|
||||
if (execp->backend->child) {
|
||||
kill(-execp->backend->child, SIGHUP);
|
||||
execp->backend->child = 0;
|
||||
}
|
||||
if (execp->backend->child_pipe >= 0) {
|
||||
close(execp->backend->child_pipe);
|
||||
execp->backend->child_pipe = -1;
|
||||
if (execp->backend->child_pipe_stdout >= 0) {
|
||||
close(execp->backend->child_pipe_stdout);
|
||||
execp->backend->child_pipe_stdout = -1;
|
||||
}
|
||||
if (execp->backend->child_pipe_stderr >= 0) {
|
||||
close(execp->backend->child_pipe_stderr);
|
||||
execp->backend->child_pipe_stderr = -1;
|
||||
}
|
||||
if (execp->backend->cmd_pids) {
|
||||
g_tree_destroy(execp->backend->cmd_pids);
|
||||
@@ -100,8 +104,8 @@ void destroy_execp(void *obj)
|
||||
free_and_null(execp->backend->uwheel_command);
|
||||
|
||||
if (execp->backend->instances) {
|
||||
fprintf(stderr, "Error: Attempt to destroy backend while there are still frontend instances!\n");
|
||||
exit(-1);
|
||||
fprintf(stderr, "tint2: Error: Attempt to destroy backend while there are still frontend instances!\n");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
free(execp->backend);
|
||||
free(execp);
|
||||
@@ -138,9 +142,11 @@ void init_execp()
|
||||
// Set missing config options
|
||||
if (!execp->backend->bg)
|
||||
execp->backend->bg = &g_array_index(backgrounds, Background, 0);
|
||||
execp->backend->buf_capacity = 1024;
|
||||
execp->backend->buf_output = calloc(execp->backend->buf_capacity, 1);
|
||||
execp->backend->text = strdup(" ");
|
||||
execp->backend->buf_stdout_capacity = 1024;
|
||||
execp->backend->buf_stdout = calloc(execp->backend->buf_stdout_capacity, 1);
|
||||
execp->backend->buf_stderr_capacity = 1024;
|
||||
execp->backend->buf_stderr = calloc(execp->backend->buf_stderr_capacity, 1);
|
||||
execp->backend->text = strdup("");
|
||||
execp->backend->icon_path = NULL;
|
||||
}
|
||||
}
|
||||
@@ -186,8 +192,9 @@ void init_execp_panel(void *p)
|
||||
execp->area.on_screen = TRUE;
|
||||
instantiate_area_gradients(&execp->area);
|
||||
|
||||
if (!execp->backend->timer)
|
||||
execp->backend->timer = add_timeout(10, 0, execp_timer_callback, execp, &execp->backend->timer);
|
||||
change_timer(&execp->backend->timer, true, 10, 0, execp_timer_callback, execp);
|
||||
|
||||
execp_update_post_read(execp);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -290,156 +297,140 @@ gboolean reload_icon(Execp *execp)
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
int execp_compute_desired_size(void *obj)
|
||||
void execp_compute_icon_text_geometry(Execp *execp,
|
||||
int *horiz_padding,
|
||||
int *vert_padding,
|
||||
int *interior_padding,
|
||||
int *icon_w,
|
||||
int *icon_h,
|
||||
gboolean *text_next_line,
|
||||
int *txt_height_ink,
|
||||
int *txt_height,
|
||||
int *txt_width,
|
||||
int *new_size,
|
||||
gboolean *resized)
|
||||
{
|
||||
Execp *execp = (Execp *)obj;
|
||||
Panel *panel = (Panel *)execp->area.panel;
|
||||
int horiz_padding = (panel_horizontal ? execp->area.paddingxlr : execp->area.paddingy);
|
||||
int vert_padding = (panel_horizontal ? execp->area.paddingy : execp->area.paddingxlr);
|
||||
int interior_padding = execp->area.paddingx;
|
||||
Area *area = &execp->area;
|
||||
*horiz_padding = (panel_horizontal ? area->paddingxlr : area->paddingy);
|
||||
*vert_padding = (panel_horizontal ? area->paddingy : area->paddingxlr);
|
||||
*interior_padding = area->paddingx;
|
||||
|
||||
int icon_w, icon_h;
|
||||
if (reload_icon(execp)) {
|
||||
if (execp->backend->icon) {
|
||||
imlib_context_set_image(execp->backend->icon);
|
||||
icon_w = imlib_image_get_width();
|
||||
icon_h = imlib_image_get_height();
|
||||
*icon_w = imlib_image_get_width();
|
||||
*icon_h = imlib_image_get_height();
|
||||
} else {
|
||||
icon_w = icon_h = 0;
|
||||
*icon_w = *icon_h = 0;
|
||||
}
|
||||
} else {
|
||||
icon_w = icon_h = 0;
|
||||
*icon_w = *icon_h = 0;
|
||||
}
|
||||
|
||||
int text_next_line = !panel_horizontal && icon_w > execp->area.width / 2;
|
||||
*text_next_line = !panel_horizontal && *icon_w > area->width / 2;
|
||||
|
||||
int available_w, available_h;
|
||||
if (panel_horizontal) {
|
||||
available_w = panel->area.width;
|
||||
available_h = area->height - 2 * area->paddingy - left_right_border_width(area);
|
||||
} else {
|
||||
available_w = !text_next_line
|
||||
? area->width - *icon_w - (*icon_w ? *interior_padding : 0) - 2 * *horiz_padding -
|
||||
left_right_border_width(area)
|
||||
: area->width - 2 * *horiz_padding - left_right_border_width(area);
|
||||
available_h = panel->area.height;
|
||||
}
|
||||
get_text_size2(execp->backend->font_desc,
|
||||
txt_height_ink,
|
||||
txt_height,
|
||||
txt_width,
|
||||
available_h,
|
||||
available_w,
|
||||
execp->backend->text,
|
||||
strlen(execp->backend->text),
|
||||
PANGO_WRAP_WORD_CHAR,
|
||||
PANGO_ELLIPSIZE_NONE,
|
||||
execp->backend->has_markup);
|
||||
|
||||
*resized = FALSE;
|
||||
if (panel_horizontal) {
|
||||
*new_size = *txt_width;
|
||||
if (*icon_w)
|
||||
*new_size += *interior_padding + *icon_w;
|
||||
*new_size += 2 * *horiz_padding + left_right_border_width(area);
|
||||
if (*new_size < area->width && abs(*new_size - area->width) < 6) {
|
||||
// we try to limit the number of resizes
|
||||
*new_size = area->width;
|
||||
*resized = TRUE;
|
||||
} else {
|
||||
*resized = *new_size != area->width;
|
||||
}
|
||||
} else {
|
||||
if (!*text_next_line) {
|
||||
*new_size = *txt_height + 2 * *vert_padding + top_bottom_border_width(area);
|
||||
*new_size = MAX(*new_size, *icon_h + 2 * *vert_padding + top_bottom_border_width(area));
|
||||
} else {
|
||||
*new_size = *icon_h + *interior_padding + *txt_height + 2 * *vert_padding + top_bottom_border_width(area);
|
||||
}
|
||||
if (*new_size != area->height) {
|
||||
*resized = TRUE;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int execp_compute_desired_size(void *obj)
|
||||
{
|
||||
Execp *execp = (Execp *)obj;
|
||||
int horiz_padding, vert_padding, interior_padding;
|
||||
int icon_w, icon_h;
|
||||
gboolean text_next_line;
|
||||
int txt_height_ink, txt_height, txt_width;
|
||||
if (panel_horizontal) {
|
||||
get_text_size2(execp->backend->font_desc,
|
||||
&txt_height_ink,
|
||||
&txt_height,
|
||||
&txt_width,
|
||||
panel->area.height,
|
||||
panel->area.width,
|
||||
execp->backend->text,
|
||||
strlen(execp->backend->text),
|
||||
PANGO_WRAP_WORD_CHAR,
|
||||
PANGO_ELLIPSIZE_NONE,
|
||||
execp->backend->has_markup);
|
||||
} else {
|
||||
get_text_size2(execp->backend->font_desc,
|
||||
&txt_height_ink,
|
||||
&txt_height,
|
||||
&txt_width,
|
||||
panel->area.height,
|
||||
!text_next_line
|
||||
? execp->area.width - icon_w - (icon_w ? interior_padding : 0) - 2 * horiz_padding -
|
||||
left_right_border_width(&execp->area)
|
||||
: execp->area.width - 2 * horiz_padding - left_right_border_width(&execp->area),
|
||||
execp->backend->text,
|
||||
strlen(execp->backend->text),
|
||||
PANGO_WRAP_WORD_CHAR,
|
||||
PANGO_ELLIPSIZE_NONE,
|
||||
execp->backend->has_markup);
|
||||
}
|
||||
int new_size;
|
||||
gboolean resized;
|
||||
execp_compute_icon_text_geometry(execp,
|
||||
&horiz_padding,
|
||||
&vert_padding,
|
||||
&interior_padding,
|
||||
&icon_w,
|
||||
&icon_h,
|
||||
&text_next_line,
|
||||
&txt_height_ink,
|
||||
&txt_height,
|
||||
&txt_width,
|
||||
&new_size,
|
||||
&resized);
|
||||
|
||||
if (panel_horizontal) {
|
||||
int new_size = txt_width;
|
||||
if (icon_w)
|
||||
new_size += interior_padding + icon_w;
|
||||
new_size += 2 * horiz_padding + left_right_border_width(&execp->area);
|
||||
return new_size;
|
||||
} else {
|
||||
int new_size;
|
||||
if (!text_next_line) {
|
||||
new_size = txt_height + 2 * vert_padding + top_bottom_border_width(&execp->area);
|
||||
new_size = MAX(new_size, icon_h + 2 * vert_padding + top_bottom_border_width(&execp->area));
|
||||
} else {
|
||||
new_size =
|
||||
icon_h + interior_padding + txt_height + 2 * vert_padding + top_bottom_border_width(&execp->area);
|
||||
}
|
||||
return new_size;
|
||||
}
|
||||
return new_size;
|
||||
}
|
||||
|
||||
gboolean resize_execp(void *obj)
|
||||
{
|
||||
Execp *execp = (Execp *)obj;
|
||||
Panel *panel = (Panel *)execp->area.panel;
|
||||
int horiz_padding = (panel_horizontal ? execp->area.paddingxlr : execp->area.paddingy);
|
||||
int vert_padding = (panel_horizontal ? execp->area.paddingy : execp->area.paddingxlr);
|
||||
int interior_padding = execp->area.paddingx;
|
||||
|
||||
int horiz_padding, vert_padding, interior_padding;
|
||||
int icon_w, icon_h;
|
||||
if (reload_icon(execp)) {
|
||||
if (execp->backend->icon) {
|
||||
imlib_context_set_image(execp->backend->icon);
|
||||
icon_w = imlib_image_get_width();
|
||||
icon_h = imlib_image_get_height();
|
||||
} else {
|
||||
icon_w = icon_h = 0;
|
||||
}
|
||||
} else {
|
||||
icon_w = icon_h = 0;
|
||||
}
|
||||
|
||||
int text_next_line = !panel_horizontal && icon_w > execp->area.width / 2;
|
||||
|
||||
gboolean text_next_line;
|
||||
int txt_height_ink, txt_height, txt_width;
|
||||
if (panel_horizontal) {
|
||||
get_text_size2(execp->backend->font_desc,
|
||||
&txt_height_ink,
|
||||
&txt_height,
|
||||
&txt_width,
|
||||
panel->area.height,
|
||||
panel->area.width,
|
||||
execp->backend->text,
|
||||
strlen(execp->backend->text),
|
||||
PANGO_WRAP_WORD_CHAR,
|
||||
PANGO_ELLIPSIZE_NONE,
|
||||
execp->backend->has_markup);
|
||||
} else {
|
||||
get_text_size2(execp->backend->font_desc,
|
||||
&txt_height_ink,
|
||||
&txt_height,
|
||||
&txt_width,
|
||||
panel->area.height,
|
||||
!text_next_line
|
||||
? execp->area.width - icon_w - (icon_w ? interior_padding : 0) - 2 * horiz_padding -
|
||||
left_right_border_width(&execp->area)
|
||||
: execp->area.width - 2 * horiz_padding - left_right_border_width(&execp->area),
|
||||
execp->backend->text,
|
||||
strlen(execp->backend->text),
|
||||
PANGO_WRAP_WORD_CHAR,
|
||||
PANGO_ELLIPSIZE_NONE,
|
||||
execp->backend->has_markup);
|
||||
}
|
||||
int new_size;
|
||||
gboolean resized;
|
||||
execp_compute_icon_text_geometry(execp,
|
||||
&horiz_padding,
|
||||
&vert_padding,
|
||||
&interior_padding,
|
||||
&icon_w,
|
||||
&icon_h,
|
||||
&text_next_line,
|
||||
&txt_height_ink,
|
||||
&txt_height,
|
||||
&txt_width,
|
||||
&new_size,
|
||||
&resized);
|
||||
|
||||
if (panel_horizontal)
|
||||
execp->area.width = new_size;
|
||||
else
|
||||
execp->area.height = new_size;
|
||||
|
||||
gboolean result = FALSE;
|
||||
if (panel_horizontal) {
|
||||
int new_size = txt_width;
|
||||
if (icon_w)
|
||||
new_size += interior_padding + icon_w;
|
||||
new_size += 2 * horiz_padding + left_right_border_width(&execp->area);
|
||||
if (new_size > execp->area.width || new_size < (execp->area.width - 6)) {
|
||||
// we try to limit the number of resize
|
||||
execp->area.width = new_size + 1;
|
||||
result = TRUE;
|
||||
}
|
||||
} else {
|
||||
int new_size;
|
||||
if (!text_next_line) {
|
||||
new_size = txt_height + 2 * vert_padding + top_bottom_border_width(&execp->area);
|
||||
new_size = MAX(new_size, icon_h + 2 * vert_padding + top_bottom_border_width(&execp->area));
|
||||
} else {
|
||||
new_size =
|
||||
icon_h + interior_padding + txt_height + 2 * vert_padding + top_bottom_border_width(&execp->area);
|
||||
}
|
||||
if (new_size != execp->area.height) {
|
||||
execp->area.height = new_size;
|
||||
result = TRUE;
|
||||
}
|
||||
}
|
||||
execp->frontend->textw = txt_width;
|
||||
execp->frontend->texth = txt_height;
|
||||
if (execp->backend->centered) {
|
||||
@@ -479,13 +470,12 @@ gboolean resize_execp(void *obj)
|
||||
}
|
||||
|
||||
schedule_redraw(&execp->area);
|
||||
|
||||
return result;
|
||||
return resized;
|
||||
}
|
||||
|
||||
void draw_execp(void *obj, cairo_t *c)
|
||||
{
|
||||
Execp *execp = obj;
|
||||
Execp *execp = (Execp *)obj;
|
||||
PangoLayout *layout = pango_cairo_create_layout(c);
|
||||
|
||||
if (execp->backend->has_icon && execp->backend->icon) {
|
||||
@@ -496,7 +486,7 @@ void draw_execp(void *obj, cairo_t *c)
|
||||
|
||||
// draw layout
|
||||
pango_layout_set_font_description(layout, execp->backend->font_desc);
|
||||
pango_layout_set_width(layout, execp->frontend->textw * PANGO_SCALE);
|
||||
pango_layout_set_width(layout, (execp->frontend->textw + 1) * 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);
|
||||
@@ -518,13 +508,13 @@ void draw_execp(void *obj, cairo_t *c)
|
||||
|
||||
void execp_dump_geometry(void *obj, int indent)
|
||||
{
|
||||
Execp *execp = obj;
|
||||
Execp *execp = (Execp *)obj;
|
||||
|
||||
if (execp->backend->has_icon && execp->backend->icon) {
|
||||
Imlib_Image tmp = imlib_context_get_image();
|
||||
imlib_context_set_image(execp->backend->icon);
|
||||
fprintf(stderr,
|
||||
"%*sIcon: x = %d, y = %d, w = %d, h = %d\n",
|
||||
"tint2: %*sIcon: x = %d, y = %d, w = %d, h = %d\n",
|
||||
indent,
|
||||
"",
|
||||
execp->frontend->iconx,
|
||||
@@ -535,7 +525,7 @@ void execp_dump_geometry(void *obj, int indent)
|
||||
imlib_context_set_image(tmp);
|
||||
}
|
||||
fprintf(stderr,
|
||||
"%*sText: x = %d, y = %d, w = %d, align = %s, text = %s\n",
|
||||
"tint2: %*sText: x = %d, y = %d, w = %d, align = %s, text = %s\n",
|
||||
indent,
|
||||
"",
|
||||
execp->frontend->textx,
|
||||
@@ -547,19 +537,17 @@ void execp_dump_geometry(void *obj, int indent)
|
||||
|
||||
void execp_force_update(Execp *execp)
|
||||
{
|
||||
if (execp->backend->child_pipe > 0) {
|
||||
if (execp->backend->child_pipe_stdout > 0) {
|
||||
// Command currently running, nothing to do
|
||||
} else {
|
||||
if (execp->backend->timer)
|
||||
stop_timeout(execp->backend->timer);
|
||||
// Run command right away
|
||||
execp->backend->timer = add_timeout(10, 0, execp_timer_callback, execp, &execp->backend->timer);
|
||||
change_timer(&execp->backend->timer, true, 10, 0, execp_timer_callback, execp);
|
||||
}
|
||||
}
|
||||
|
||||
void execp_action(void *obj, int button, int x, int y, Time time)
|
||||
{
|
||||
Execp *execp = obj;
|
||||
Execp *execp = (Execp *)obj;
|
||||
char *command = NULL;
|
||||
switch (button) {
|
||||
case 1:
|
||||
@@ -579,17 +567,15 @@ void execp_action(void *obj, int button, int x, int y, Time time)
|
||||
break;
|
||||
}
|
||||
if (command) {
|
||||
char *full_cmd = g_strdup_printf("export EXECP_X=%d;"
|
||||
"export EXECP_Y=%d;"
|
||||
"export EXECP_W=%d;"
|
||||
"export EXECP_H=%d; %s",
|
||||
x,
|
||||
y,
|
||||
execp->area.width,
|
||||
execp->area.height,
|
||||
command);
|
||||
pid_t pid = tint_exec(full_cmd, NULL, NULL, time, obj, x, y);
|
||||
g_free(full_cmd);
|
||||
setenvd("EXECP_X", x);
|
||||
setenvd("EXECP_Y", y);
|
||||
setenvd("EXECP_W", execp->area.width);
|
||||
setenvd("EXECP_H", execp->area.height);
|
||||
pid_t pid = tint_exec(command, NULL, NULL, time, obj, x, y, FALSE, TRUE);
|
||||
unsetenv("EXECP_X");
|
||||
unsetenv("EXECP_Y");
|
||||
unsetenv("EXECP_W");
|
||||
unsetenv("EXECP_H");
|
||||
if (pid > 0)
|
||||
g_tree_insert(execp->backend->cmd_pids, GINT_TO_POINTER(pid), GINT_TO_POINTER(1));
|
||||
} else {
|
||||
@@ -605,78 +591,93 @@ void execp_cmd_completed(Execp *execp, pid_t pid)
|
||||
|
||||
void execp_timer_callback(void *arg)
|
||||
{
|
||||
Execp *execp = arg;
|
||||
Execp *execp = (Execp *)arg;
|
||||
|
||||
if (!execp->backend->command)
|
||||
return;
|
||||
|
||||
// Still running!
|
||||
if (execp->backend->child_pipe > 0)
|
||||
if (execp->backend->child_pipe_stdout > 0)
|
||||
return;
|
||||
|
||||
int pipe_fd[2];
|
||||
if (pipe(pipe_fd)) {
|
||||
int pipe_fd_stdout[2];
|
||||
if (pipe(pipe_fd_stdout)) {
|
||||
// TODO maybe write this in tooltip, but if this happens we're screwed anyways
|
||||
fprintf(stderr, "Execp: Creating pipe failed!\n");
|
||||
fprintf(stderr, "tint2: Execp: Creating pipe failed!\n");
|
||||
return;
|
||||
}
|
||||
|
||||
fcntl(pipe_fd[0], F_SETFL, O_NONBLOCK | fcntl(pipe_fd[0], F_GETFL));
|
||||
fcntl(pipe_fd_stdout[0], F_SETFL, O_NONBLOCK | fcntl(pipe_fd_stdout[0], F_GETFL));
|
||||
|
||||
int pipe_fd_stderr[2];
|
||||
if (pipe(pipe_fd_stderr)) {
|
||||
close(pipe_fd_stdout[1]);
|
||||
close(pipe_fd_stdout[0]);
|
||||
// TODO maybe write this in tooltip, but if this happens we're screwed anyways
|
||||
fprintf(stderr, "tint2: Execp: Creating pipe failed!\n");
|
||||
return;
|
||||
}
|
||||
|
||||
fcntl(pipe_fd_stderr[0], F_SETFL, O_NONBLOCK | fcntl(pipe_fd_stderr[0], F_GETFL));
|
||||
|
||||
// Fork and run command, capturing stdout in pipe
|
||||
pid_t child = fork();
|
||||
if (child == -1) {
|
||||
// TODO maybe write this in tooltip, but if this happens we're screwed anyways
|
||||
fprintf(stderr, "Fork failed.\n");
|
||||
close(pipe_fd[1]);
|
||||
close(pipe_fd[0]);
|
||||
fprintf(stderr, "tint2: Fork failed.\n");
|
||||
close(pipe_fd_stdout[1]);
|
||||
close(pipe_fd_stdout[0]);
|
||||
close(pipe_fd_stderr[1]);
|
||||
close(pipe_fd_stderr[0]);
|
||||
return;
|
||||
} else if (child == 0) {
|
||||
fprintf(stderr, "Executing: %s\n", execp->backend->command);
|
||||
fprintf(stderr, "tint2: Executing: %s\n", execp->backend->command);
|
||||
// We are in the child
|
||||
close(pipe_fd[0]);
|
||||
dup2(pipe_fd[1], 1); // 1 is stdout
|
||||
close(pipe_fd[1]);
|
||||
close(pipe_fd_stdout[0]);
|
||||
dup2(pipe_fd_stdout[1], 1); // 1 is stdout
|
||||
close(pipe_fd_stdout[1]);
|
||||
close(pipe_fd_stderr[0]);
|
||||
dup2(pipe_fd_stderr[1], 2); // 2 is stderr
|
||||
close(pipe_fd_stderr[1]);
|
||||
close_all_fds();
|
||||
setpgid(0, 0);
|
||||
execl("/bin/sh", "/bin/sh", "-c", execp->backend->command, NULL);
|
||||
// This should never happen!
|
||||
fprintf(stdout, "execl() failed\nexecl() failed\n");
|
||||
fflush(stdout);
|
||||
fprintf(stderr, "execl() failed\nexecl() failed\n");
|
||||
exit(0);
|
||||
}
|
||||
close(pipe_fd[1]);
|
||||
close(pipe_fd_stdout[1]);
|
||||
close(pipe_fd_stderr[1]);
|
||||
execp->backend->child = child;
|
||||
execp->backend->child_pipe = pipe_fd[0];
|
||||
execp->backend->buf_length = 0;
|
||||
execp->backend->buf_output[execp->backend->buf_length] = '\0';
|
||||
execp->backend->child_pipe_stdout = pipe_fd_stdout[0];
|
||||
execp->backend->child_pipe_stderr = pipe_fd_stderr[0];
|
||||
execp->backend->buf_stdout_length = 0;
|
||||
execp->backend->buf_stdout[execp->backend->buf_stdout_length] = '\0';
|
||||
execp->backend->buf_stderr_length = 0;
|
||||
execp->backend->buf_stderr[execp->backend->buf_stderr_length] = '\0';
|
||||
execp->backend->last_update_start_time = time(NULL);
|
||||
}
|
||||
|
||||
gboolean read_execp(void *obj)
|
||||
void read_from_pipe(int fd, char **buffer, ssize_t *buffer_length, ssize_t *buffer_capacity, gboolean *eof)
|
||||
{
|
||||
Execp *execp = (Execp *)obj;
|
||||
|
||||
if (execp->backend->child_pipe < 0)
|
||||
return FALSE;
|
||||
|
||||
gboolean command_finished = FALSE;
|
||||
*eof = FALSE;
|
||||
while (1) {
|
||||
// Make sure there is free space in the buffer
|
||||
if (execp->backend->buf_capacity - execp->backend->buf_length < 1024) {
|
||||
execp->backend->buf_capacity *= 2;
|
||||
execp->backend->buf_output = realloc(execp->backend->buf_output, execp->backend->buf_capacity);
|
||||
if (*buffer_capacity - *buffer_length < 1024) {
|
||||
*buffer_capacity *= 2;
|
||||
*buffer = (char *)realloc(*buffer, *buffer_capacity);
|
||||
}
|
||||
ssize_t count = read(execp->backend->child_pipe,
|
||||
execp->backend->buf_output + execp->backend->buf_length,
|
||||
execp->backend->buf_capacity - execp->backend->buf_length - 1);
|
||||
ssize_t count = read(fd,
|
||||
*buffer + *buffer_length,
|
||||
*buffer_capacity - *buffer_length - 1);
|
||||
if (count > 0) {
|
||||
// Successful read
|
||||
execp->backend->buf_length += count;
|
||||
execp->backend->buf_output[execp->backend->buf_length] = '\0';
|
||||
*buffer_length += count;
|
||||
(*buffer)[*buffer_length] = '\0';
|
||||
continue;
|
||||
} else if (count == 0) {
|
||||
// End of file
|
||||
command_finished = TRUE;
|
||||
*eof = TRUE;
|
||||
break;
|
||||
} else if (errno == EAGAIN || errno == EWOULDBLOCK) {
|
||||
// No more data available at the moment
|
||||
@@ -686,28 +687,86 @@ gboolean read_execp(void *obj)
|
||||
continue;
|
||||
} else {
|
||||
// Error
|
||||
command_finished = TRUE;
|
||||
*eof = TRUE;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
gboolean starts_with(char *s, char *prefix)
|
||||
{
|
||||
char *p, *q;
|
||||
for (p = s, q = prefix; *p && *q; p++, q++) {
|
||||
if (*p != *q)
|
||||
return FALSE;
|
||||
}
|
||||
return *q == '\0';
|
||||
}
|
||||
|
||||
char *last_substring(char *s, char *sub)
|
||||
{
|
||||
char *result = NULL;
|
||||
for (char *p = s; *p; p++) {
|
||||
if (starts_with(p, sub))
|
||||
result = p;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
void rstrip(char *s)
|
||||
{
|
||||
size_t len = strlen(s);
|
||||
while (len > 0) {
|
||||
if (s[len-1] == ' ' || s[len-1] == '\n') {
|
||||
s[len-1] = 0;
|
||||
len--;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
gboolean read_execp(void *obj)
|
||||
{
|
||||
Execp *execp = (Execp *)obj;
|
||||
|
||||
if (execp->backend->child_pipe_stdout < 0)
|
||||
return FALSE;
|
||||
|
||||
gboolean stdout_eof, stderr_eof;
|
||||
read_from_pipe(execp->backend->child_pipe_stdout,
|
||||
&execp->backend->buf_stdout,
|
||||
&execp->backend->buf_stdout_length,
|
||||
&execp->backend->buf_stdout_capacity,
|
||||
&stdout_eof);
|
||||
read_from_pipe(execp->backend->child_pipe_stderr,
|
||||
&execp->backend->buf_stderr,
|
||||
&execp->backend->buf_stderr_length,
|
||||
&execp->backend->buf_stderr_capacity,
|
||||
&stderr_eof);
|
||||
|
||||
gboolean command_finished = stdout_eof && stderr_eof;
|
||||
|
||||
if (command_finished) {
|
||||
execp->backend->child = 0;
|
||||
close(execp->backend->child_pipe);
|
||||
execp->backend->child_pipe = -1;
|
||||
close(execp->backend->child_pipe_stdout);
|
||||
execp->backend->child_pipe_stdout = -1;
|
||||
close(execp->backend->child_pipe_stderr);
|
||||
execp->backend->child_pipe_stderr = -1;
|
||||
if (execp->backend->interval)
|
||||
execp->backend->timer =
|
||||
add_timeout(execp->backend->interval * 1000, 0, execp_timer_callback, execp, &execp->backend->timer);
|
||||
change_timer(&execp->backend->timer, true, execp->backend->interval * 1000, 0, execp_timer_callback, execp);
|
||||
}
|
||||
|
||||
char *ansi_clear_screen = (char*)"\x1b[2J";
|
||||
if (!execp->backend->continuous && command_finished) {
|
||||
// Handle stdout
|
||||
free_and_null(execp->backend->text);
|
||||
free_and_null(execp->backend->icon_path);
|
||||
if (!execp->backend->has_icon) {
|
||||
execp->backend->text = strdup(execp->backend->buf_output);
|
||||
execp->backend->text = strdup(execp->backend->buf_stdout);
|
||||
} else {
|
||||
char *text = strchr(execp->backend->buf_output, '\n');
|
||||
char *text = strchr(execp->backend->buf_stdout, '\n');
|
||||
if (text) {
|
||||
*text = '\0';
|
||||
text++;
|
||||
@@ -715,41 +774,75 @@ gboolean read_execp(void *obj)
|
||||
} else {
|
||||
execp->backend->text = strdup("");
|
||||
}
|
||||
execp->backend->icon_path = strdup(execp->backend->buf_output);
|
||||
execp->backend->icon_path = strdup(execp->backend->buf_stdout);
|
||||
}
|
||||
int len = strlen(execp->backend->text);
|
||||
if (len > 0 && execp->backend->text[len - 1] == '\n')
|
||||
execp->backend->text[len - 1] = '\0';
|
||||
execp->backend->buf_length = 0;
|
||||
execp->backend->buf_output[execp->backend->buf_length] = '\0';
|
||||
execp->backend->buf_stdout_length = 0;
|
||||
execp->backend->buf_stdout[execp->backend->buf_stdout_length] = '\0';
|
||||
// Handle stderr
|
||||
if (!execp->backend->has_user_tooltip) {
|
||||
free_and_null(execp->backend->tooltip);
|
||||
char *start = last_substring(execp->backend->buf_stderr, ansi_clear_screen);
|
||||
if (start)
|
||||
start += strlen(ansi_clear_screen);
|
||||
else
|
||||
start = execp->backend->buf_stderr;
|
||||
if (*start) {
|
||||
execp->backend->tooltip = strdup(start);
|
||||
rstrip(execp->backend->tooltip);
|
||||
if (strlen(execp->backend->tooltip) > MAX_TOOLTIP_LEN)
|
||||
execp->backend->tooltip[MAX_TOOLTIP_LEN] = '\0';
|
||||
}
|
||||
}
|
||||
execp->backend->buf_stderr_length = 0;
|
||||
execp->backend->buf_stderr[execp->backend->buf_stderr_length] = '\0';
|
||||
//
|
||||
execp->backend->last_update_finish_time = time(NULL);
|
||||
execp->backend->last_update_duration =
|
||||
execp->backend->last_update_finish_time - execp->backend->last_update_start_time;
|
||||
return TRUE;
|
||||
} else if (execp->backend->continuous > 0) {
|
||||
// Handle stderr
|
||||
if (!execp->backend->has_user_tooltip) {
|
||||
free_and_null(execp->backend->tooltip);
|
||||
char *start = last_substring(execp->backend->buf_stderr, ansi_clear_screen);
|
||||
if (start) {
|
||||
start += strlen(ansi_clear_screen);
|
||||
memmove(execp->backend->buf_stderr, start, strlen(start) + 1);
|
||||
execp->backend->buf_stderr_length = (ssize_t)strlen(execp->backend->buf_stderr);
|
||||
}
|
||||
if (execp->backend->buf_stderr_length > MAX_TOOLTIP_LEN) {
|
||||
execp->backend->buf_stderr_length = MAX_TOOLTIP_LEN;
|
||||
execp->backend->buf_stderr[execp->backend->buf_stderr_length] = '\0';
|
||||
}
|
||||
execp->backend->tooltip = strdup(execp->backend->buf_stderr);
|
||||
rstrip(execp->backend->tooltip);
|
||||
} else {
|
||||
execp->backend->buf_stderr_length = 0;
|
||||
execp->backend->buf_stderr[execp->backend->buf_stderr_length] = '\0';
|
||||
}
|
||||
// Handle stdout
|
||||
// Count lines in buffer
|
||||
int num_lines = 0;
|
||||
char *last = execp->backend->buf_output;
|
||||
char *end = NULL;
|
||||
for (char *c = execp->backend->buf_output; *c; c++) {
|
||||
for (char *c = execp->backend->buf_stdout; *c; c++) {
|
||||
if (*c == '\n') {
|
||||
num_lines++;
|
||||
if (num_lines == execp->backend->continuous)
|
||||
end = c;
|
||||
}
|
||||
last = c;
|
||||
}
|
||||
if (*last && *last != '\n')
|
||||
num_lines++;
|
||||
if (num_lines >= execp->backend->continuous) {
|
||||
if (end)
|
||||
*end = '\0';
|
||||
free_and_null(execp->backend->text);
|
||||
free_and_null(execp->backend->icon_path);
|
||||
if (!execp->backend->has_icon) {
|
||||
execp->backend->text = strdup(execp->backend->buf_output);
|
||||
execp->backend->text = strdup(execp->backend->buf_stdout);
|
||||
} else {
|
||||
char *text = strchr(execp->backend->buf_output, '\n');
|
||||
char *text = strchr(execp->backend->buf_stdout, '\n');
|
||||
if (text) {
|
||||
*text = '\0';
|
||||
text++;
|
||||
@@ -757,23 +850,23 @@ gboolean read_execp(void *obj)
|
||||
} else {
|
||||
execp->backend->text = strdup("");
|
||||
}
|
||||
execp->backend->icon_path = strdup(execp->backend->buf_output);
|
||||
execp->backend->icon_path = strdup(execp->backend->buf_stdout);
|
||||
}
|
||||
int len = strlen(execp->backend->text);
|
||||
size_t len = strlen(execp->backend->text);
|
||||
if (len > 0 && execp->backend->text[len - 1] == '\n')
|
||||
execp->backend->text[len - 1] = '\0';
|
||||
|
||||
if (end) {
|
||||
char *next = end + 1;
|
||||
int copied = next - execp->backend->buf_output;
|
||||
int remaining = execp->backend->buf_length - copied;
|
||||
ssize_t copied = next - execp->backend->buf_stdout;
|
||||
ssize_t remaining = execp->backend->buf_stdout_length - copied;
|
||||
if (remaining > 0) {
|
||||
memmove(execp->backend->buf_output, next, remaining);
|
||||
execp->backend->buf_length = remaining;
|
||||
execp->backend->buf_output[execp->backend->buf_length] = '\0';
|
||||
memmove(execp->backend->buf_stdout, next, (size_t)remaining);
|
||||
execp->backend->buf_stdout_length = remaining;
|
||||
execp->backend->buf_stdout[execp->backend->buf_stdout_length] = '\0';
|
||||
} else {
|
||||
execp->backend->buf_length = 0;
|
||||
execp->backend->buf_output[execp->backend->buf_length] = '\0';
|
||||
execp->backend->buf_stdout_length = 0;
|
||||
execp->backend->buf_stdout[execp->backend->buf_stdout_length] = '\0';
|
||||
}
|
||||
}
|
||||
|
||||
@@ -786,29 +879,29 @@ gboolean read_execp(void *obj)
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
const char *time_to_string(int seconds, char *buffer)
|
||||
const char *time_to_string(int seconds, char *buffer, size_t buffer_size)
|
||||
{
|
||||
if (seconds < 60) {
|
||||
sprintf(buffer, "%ds", seconds);
|
||||
snprintf(buffer, buffer_size, "%ds", seconds);
|
||||
} else if (seconds < 60 * 60) {
|
||||
int m = seconds / 60;
|
||||
seconds = seconds % 60;
|
||||
int s = seconds;
|
||||
sprintf(buffer, "%d:%ds", m, s);
|
||||
snprintf(buffer, buffer_size, "%d:%ds", m, s);
|
||||
} else {
|
||||
int h = seconds / (60 * 60);
|
||||
seconds = seconds % (60 * 60);
|
||||
int m = seconds / 60;
|
||||
seconds = seconds % 60;
|
||||
int s = seconds;
|
||||
sprintf(buffer, "%d:%d:%ds", h, m, s);
|
||||
snprintf(buffer, buffer_size, "%d:%d:%ds", h, m, s);
|
||||
}
|
||||
return buffer;
|
||||
}
|
||||
|
||||
char *execp_get_tooltip(void *obj)
|
||||
{
|
||||
Execp *execp = obj;
|
||||
Execp *execp = (Execp *)obj;
|
||||
|
||||
if (execp->backend->tooltip) {
|
||||
if (strlen(execp->backend->tooltip) > 0)
|
||||
@@ -822,41 +915,86 @@ char *execp_get_tooltip(void *obj)
|
||||
char tmp_buf1[256];
|
||||
char tmp_buf2[256];
|
||||
char tmp_buf3[256];
|
||||
if (execp->backend->child_pipe < 0) {
|
||||
if (execp->backend->child_pipe_stdout < 0) {
|
||||
// Not executing command
|
||||
if (execp->backend->last_update_finish_time) {
|
||||
// We updated at least once
|
||||
if (execp->backend->interval > 0) {
|
||||
sprintf(execp->backend->tooltip_text,
|
||||
"Last update finished %s ago (took %s). Next update starting in %s.",
|
||||
time_to_string((int)(now - execp->backend->last_update_finish_time), tmp_buf1),
|
||||
time_to_string((int)execp->backend->last_update_duration, tmp_buf2),
|
||||
time_to_string((int)(execp->backend->interval - (now - execp->backend->last_update_finish_time)),
|
||||
tmp_buf3));
|
||||
snprintf(execp->backend->tooltip_text,
|
||||
sizeof(execp->backend->tooltip_text),
|
||||
"Last update finished %s ago (took %s). Next update starting in %s.",
|
||||
time_to_string((int)(now - execp->backend->last_update_finish_time), tmp_buf1, sizeof(tmp_buf1)),
|
||||
time_to_string((int)execp->backend->last_update_duration, tmp_buf2, sizeof(tmp_buf2)),
|
||||
time_to_string((int)(execp->backend->interval - (now - execp->backend->last_update_finish_time)),
|
||||
tmp_buf3, sizeof(tmp_buf3)));
|
||||
} else {
|
||||
sprintf(execp->backend->tooltip_text,
|
||||
"Last update finished %s ago (took %s).",
|
||||
time_to_string((int)(now - execp->backend->last_update_finish_time), tmp_buf1),
|
||||
time_to_string((int)execp->backend->last_update_duration, tmp_buf2));
|
||||
snprintf(execp->backend->tooltip_text,
|
||||
sizeof(execp->backend->tooltip_text),
|
||||
"Last update finished %s ago (took %s).",
|
||||
time_to_string((int)(now - execp->backend->last_update_finish_time), tmp_buf1, sizeof(tmp_buf1)),
|
||||
time_to_string((int)execp->backend->last_update_duration, tmp_buf2, sizeof(tmp_buf2)));
|
||||
}
|
||||
} else {
|
||||
// we never requested an update
|
||||
sprintf(execp->backend->tooltip_text, "Never updated. No update scheduled.");
|
||||
snprintf(execp->backend->tooltip_text, sizeof(execp->backend->tooltip_text), "Never updated. No update scheduled.");
|
||||
}
|
||||
} else {
|
||||
// Currently executing command
|
||||
if (execp->backend->last_update_finish_time) {
|
||||
// we finished updating at least once
|
||||
sprintf(execp->backend->tooltip_text,
|
||||
"Last update finished %s ago. Update in progress (started %s ago).",
|
||||
time_to_string((int)(now - execp->backend->last_update_finish_time), tmp_buf1),
|
||||
time_to_string((int)(now - execp->backend->last_update_start_time), tmp_buf3));
|
||||
snprintf(execp->backend->tooltip_text,
|
||||
sizeof(execp->backend->tooltip_text),
|
||||
"Last update finished %s ago. Update in progress (started %s ago).",
|
||||
time_to_string((int)(now - execp->backend->last_update_finish_time), tmp_buf1, sizeof(tmp_buf1)),
|
||||
time_to_string((int)(now - execp->backend->last_update_start_time), tmp_buf3, sizeof(tmp_buf3)));
|
||||
} else {
|
||||
// we never finished an update
|
||||
sprintf(execp->backend->tooltip_text,
|
||||
"First update in progress (started %s seconds ago).",
|
||||
time_to_string((int)(now - execp->backend->last_update_start_time), tmp_buf1));
|
||||
snprintf(execp->backend->tooltip_text,
|
||||
sizeof(execp->backend->tooltip_text),
|
||||
"First update in progress (started %s seconds ago).",
|
||||
time_to_string((int)(now - execp->backend->last_update_start_time), tmp_buf1, sizeof(tmp_buf1)));
|
||||
}
|
||||
}
|
||||
return strdup(execp->backend->tooltip_text);
|
||||
}
|
||||
|
||||
void execp_update_post_read(Execp *execp)
|
||||
{
|
||||
int icon_h, icon_w;
|
||||
if (reload_icon(execp)) {
|
||||
if (execp->backend->icon) {
|
||||
imlib_context_set_image(execp->backend->icon);
|
||||
icon_w = imlib_image_get_width();
|
||||
icon_h = imlib_image_get_height();
|
||||
} else {
|
||||
icon_w = icon_h = 0;
|
||||
}
|
||||
} else {
|
||||
icon_w = icon_h = 0;
|
||||
}
|
||||
|
||||
if ((icon_h == 0 || icon_w == 0) && execp->backend->text[0] == 0) {
|
||||
// Easy to test with bash -c 'R=$(( RANDOM % 2 )); [ $R -eq 0 ] && echo HELLO $R'
|
||||
if (execp->area.on_screen)
|
||||
hide(&execp->area);
|
||||
} else {
|
||||
if (!execp->area.on_screen)
|
||||
show(&execp->area);
|
||||
execp->area.resize_needed = TRUE;
|
||||
schedule_panel_redraw();
|
||||
}
|
||||
}
|
||||
|
||||
void handle_execp_events()
|
||||
{
|
||||
for (GList *l = panel_config.execp_list; l; l = l->next) {
|
||||
Execp *execp = (Execp *)l->data;
|
||||
if (read_execp(execp)) {
|
||||
GList *l_instance;
|
||||
for (l_instance = execp->backend->instances; l_instance; l_instance = l_instance->next) {
|
||||
Execp *instance = (Execp *)l_instance->data;
|
||||
execp_update_post_read(instance);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -27,6 +27,7 @@ typedef struct ExecpBackend {
|
||||
gboolean cache_icon;
|
||||
int icon_w;
|
||||
int icon_h;
|
||||
gboolean has_user_tooltip;
|
||||
char *tooltip;
|
||||
gboolean centered;
|
||||
gboolean has_font;
|
||||
@@ -45,21 +46,25 @@ typedef struct ExecpBackend {
|
||||
Background *bg;
|
||||
|
||||
// Backend state:
|
||||
timeout *timer;
|
||||
int child_pipe;
|
||||
Timer timer;
|
||||
int child_pipe_stdout;
|
||||
int child_pipe_stderr;
|
||||
pid_t child;
|
||||
|
||||
// Command output buffer
|
||||
char *buf_output;
|
||||
int buf_length;
|
||||
int buf_capacity;
|
||||
char *buf_stdout;
|
||||
ssize_t buf_stdout_length;
|
||||
ssize_t buf_stdout_capacity;
|
||||
char *buf_stderr;
|
||||
ssize_t buf_stderr_length;
|
||||
ssize_t buf_stderr_capacity;
|
||||
|
||||
// Text extracted from the output buffer
|
||||
char *text;
|
||||
// Icon path extracted from the output buffer
|
||||
char *icon_path;
|
||||
Imlib_Image icon;
|
||||
char tooltip_text[512];
|
||||
gchar tooltip_text[512];
|
||||
|
||||
// The time the last command was started
|
||||
time_t last_update_start_time;
|
||||
@@ -138,6 +143,11 @@ void execp_cmd_completed(Execp *obj, pid_t pid);
|
||||
// Returns 1 if the output has been updated and a redraw is needed.
|
||||
gboolean read_execp(void *obj);
|
||||
|
||||
// Called for Execp front elements when the command output has changed.
|
||||
void execp_update_post_read(Execp *execp);
|
||||
|
||||
void execp_default_font_changed();
|
||||
|
||||
void handle_execp_events();
|
||||
|
||||
#endif // EXECPLUGIN_H
|
||||
|
||||
310
src/init.c
Normal file
310
src/init.c
Normal file
@@ -0,0 +1,310 @@
|
||||
#include "init.h"
|
||||
|
||||
#include <locale.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <X11/Xlib.h>
|
||||
#include <X11/Xutil.h>
|
||||
#include <X11/Xatom.h>
|
||||
#include <X11/extensions/XShm.h>
|
||||
|
||||
#include "config.h"
|
||||
#include "default_icon.h"
|
||||
#include "drag_and_drop.h"
|
||||
#include "fps_distribution.h"
|
||||
#include "panel.h"
|
||||
#include "server.h"
|
||||
#include "signals.h"
|
||||
#include "test.h"
|
||||
#include "tooltip.h"
|
||||
#include "tracing.h"
|
||||
#include "uevent.h"
|
||||
#include "version.h"
|
||||
|
||||
void print_usage()
|
||||
{
|
||||
fprintf(stdout,
|
||||
"Usage: tint2 [OPTION...]\n"
|
||||
"\n"
|
||||
"Options:\n"
|
||||
" -c path_to_config_file Loads the configuration file from a\n"
|
||||
" custom location.\n"
|
||||
" -v, --version Prints version information and exits.\n"
|
||||
" -h, --help Display this help and exits.\n"
|
||||
"\n"
|
||||
"For more information, run `man tint2` or visit the project page\n"
|
||||
"<https://gitlab.com/o9000/tint2>.\n");
|
||||
}
|
||||
|
||||
void handle_cli_arguments(int argc, char **argv)
|
||||
{
|
||||
// Read command line arguments
|
||||
for (int i = 1; i < argc; ++i) {
|
||||
gboolean error = FALSE;
|
||||
if (strcmp(argv[i], "-h") == 0 || strcmp(argv[i], "--help") == 0) {
|
||||
print_usage();
|
||||
exit(0);
|
||||
} else if (strcmp(argv[i], "-v") == 0 || strcmp(argv[i], "--version") == 0) {
|
||||
fprintf(stdout, "tint2 version %s\n", VERSION_STRING);
|
||||
exit(0);
|
||||
} else if (strcmp(argv[i], "--test") == 0) {
|
||||
run_all_tests(false);
|
||||
exit(0);
|
||||
} else if (strcmp(argv[i], "--test-verbose") == 0) {
|
||||
run_all_tests(true);
|
||||
exit(0);
|
||||
} else if (strcmp(argv[i], "--dump-image-data") == 0) {
|
||||
dump_image_data(argv[i+1], argv[i+2]);
|
||||
exit(0);
|
||||
} else if (strcmp(argv[i], "-c") == 0) {
|
||||
if (i + 1 < argc) {
|
||||
i++;
|
||||
config_path = strdup(argv[i]);
|
||||
} else {
|
||||
error = TRUE;
|
||||
}
|
||||
} else if (strcmp(argv[i], "-s") == 0) {
|
||||
if (i + 1 < argc) {
|
||||
i++;
|
||||
snapshot_path = strdup(argv[i]);
|
||||
} else {
|
||||
error = TRUE;
|
||||
}
|
||||
} else if (i + 1 == argc) {
|
||||
config_path = strdup(argv[i]);
|
||||
}
|
||||
#ifdef ENABLE_BATTERY
|
||||
else if (strcmp(argv[i], "--battery-sys-prefix") == 0) {
|
||||
if (i + 1 < argc) {
|
||||
i++;
|
||||
battery_sys_prefix = strdup(argv[i]);
|
||||
} else {
|
||||
error = TRUE;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
else {
|
||||
error = TRUE;
|
||||
}
|
||||
if (error) {
|
||||
print_usage();
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void handle_env_vars()
|
||||
{
|
||||
debug_geometry = getenv("DEBUG_GEOMETRY") != NULL;
|
||||
debug_gradients = getenv("DEBUG_GRADIENTS") != NULL;
|
||||
debug_icons = getenv("DEBUG_ICONS") != NULL;
|
||||
debug_fps = getenv("DEBUG_FPS") != NULL;
|
||||
debug_frames = getenv("DEBUG_FRAMES") != NULL;
|
||||
debug_dnd = getenv("DEBUG_DND") != NULL;
|
||||
debug_thumbnails = getenv("DEBUG_THUMBNAILS") != NULL;
|
||||
debug_timers = getenv("DEBUG_TIMERS") != NULL;
|
||||
if (debug_fps) {
|
||||
init_fps_distribution();
|
||||
char *s = getenv("TRACING_FPS_THRESHOLD");
|
||||
if (!s || sscanf(s, "%lf", &tracing_fps_threshold) != 1) {
|
||||
tracing_fps_threshold = 60;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static Timer detect_compositor_timer = DEFAULT_TIMER;
|
||||
static int detect_compositor_timer_counter = 0;
|
||||
|
||||
void detect_compositor(void *arg)
|
||||
{
|
||||
if (server.composite_manager) {
|
||||
stop_timer(&detect_compositor_timer);
|
||||
return;
|
||||
}
|
||||
|
||||
detect_compositor_timer_counter--;
|
||||
if (detect_compositor_timer_counter < 0) {
|
||||
stop_timer(&detect_compositor_timer);
|
||||
return;
|
||||
}
|
||||
|
||||
// No compositor, check for one
|
||||
if (XGetSelectionOwner(server.display, server.atom._NET_WM_CM_S0) != None) {
|
||||
stop_timer(&detect_compositor_timer);
|
||||
// Restart tint2
|
||||
fprintf(stderr, "tint2: Detected compositor, restarting tint2...\n");
|
||||
kill(getpid(), SIGUSR1);
|
||||
}
|
||||
}
|
||||
|
||||
void start_detect_compositor()
|
||||
{
|
||||
// Already have a compositor, nothing to do
|
||||
if (server.composite_manager)
|
||||
return;
|
||||
|
||||
// Check every 0.5 seconds for up to 30 seconds
|
||||
detect_compositor_timer_counter = 60;
|
||||
INIT_TIMER(detect_compositor_timer);
|
||||
change_timer(&detect_compositor_timer, true, 500, 500, detect_compositor, 0);
|
||||
}
|
||||
|
||||
void create_default_elements()
|
||||
{
|
||||
default_timers();
|
||||
default_systray();
|
||||
memset(&server, 0, sizeof(server));
|
||||
#ifdef ENABLE_BATTERY
|
||||
default_battery();
|
||||
#endif
|
||||
default_clock();
|
||||
default_launcher();
|
||||
default_taskbar();
|
||||
default_tooltip();
|
||||
default_execp();
|
||||
default_button();
|
||||
default_panel();
|
||||
}
|
||||
|
||||
void load_default_task_icon()
|
||||
{
|
||||
const gchar *const *data_dirs = g_get_system_data_dirs();
|
||||
for (int i = 0; data_dirs[i] != NULL; i++) {
|
||||
gchar *path = g_build_filename(data_dirs[i], "tint2", "default_icon.png", NULL);
|
||||
if (g_file_test(path, G_FILE_TEST_EXISTS))
|
||||
default_icon = load_image(path, TRUE);
|
||||
g_free(path);
|
||||
}
|
||||
if (!default_icon) {
|
||||
default_icon = imlib_create_image_using_data(default_icon_width,
|
||||
default_icon_height,
|
||||
default_icon_data);
|
||||
}
|
||||
}
|
||||
|
||||
void init_post_config()
|
||||
{
|
||||
server_init_visual();
|
||||
server_init_xdamage();
|
||||
|
||||
imlib_context_set_display(server.display);
|
||||
imlib_context_set_visual(server.visual);
|
||||
imlib_context_set_colormap(server.colormap);
|
||||
|
||||
init_signals_postconfig();
|
||||
load_default_task_icon();
|
||||
|
||||
XSync(server.display, False);
|
||||
}
|
||||
|
||||
void init_X11_pre_config()
|
||||
{
|
||||
server.display = XOpenDisplay(NULL);
|
||||
if (!server.display) {
|
||||
fprintf(stderr, "tint2: could not open display!\n");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
server.x11_fd = ConnectionNumber(server.display);
|
||||
XSetErrorHandler((XErrorHandler)server_catch_error);
|
||||
XSetIOErrorHandler((XIOErrorHandler)x11_io_error);
|
||||
server_init_atoms();
|
||||
server.screen = DefaultScreen(server.display);
|
||||
server.root_win = RootWindow(server.display, server.screen);
|
||||
server.desktop = get_current_desktop();
|
||||
server.has_shm = XShmQueryExtension(server.display);
|
||||
|
||||
// Needed since the config file uses '.' as decimal separator
|
||||
setlocale(LC_ALL, "");
|
||||
setlocale(LC_NUMERIC, "POSIX");
|
||||
|
||||
/* Catch events */
|
||||
XSelectInput(server.display, server.root_win, PropertyChangeMask | StructureNotifyMask);
|
||||
|
||||
// get monitor and desktop config
|
||||
get_monitors();
|
||||
get_desktops();
|
||||
|
||||
server.disable_transparency = 0;
|
||||
|
||||
xsettings_client = xsettings_client_new(server.display, server.screen, xsettings_notify_cb, NULL, NULL);
|
||||
}
|
||||
|
||||
void init(int argc, char **argv)
|
||||
{
|
||||
setlinebuf(stdout);
|
||||
setlinebuf(stderr);
|
||||
default_config();
|
||||
handle_env_vars();
|
||||
handle_cli_arguments(argc, argv);
|
||||
create_default_elements();
|
||||
init_signals();
|
||||
|
||||
init_X11_pre_config();
|
||||
if (!config_read()) {
|
||||
fprintf(stderr, "tint2: Could not read config file.\n");
|
||||
print_usage();
|
||||
cleanup();
|
||||
return;
|
||||
}
|
||||
|
||||
init_post_config();
|
||||
start_detect_compositor();
|
||||
init_panel();
|
||||
}
|
||||
|
||||
void cleanup()
|
||||
{
|
||||
#ifdef HAVE_SN
|
||||
if (startup_notifications) {
|
||||
sn_display_unref(server.sn_display);
|
||||
server.sn_display = NULL;
|
||||
}
|
||||
#endif // HAVE_SN
|
||||
|
||||
cleanup_button();
|
||||
cleanup_execp();
|
||||
cleanup_systray();
|
||||
cleanup_tooltip();
|
||||
cleanup_clock();
|
||||
cleanup_launcher();
|
||||
#ifdef ENABLE_BATTERY
|
||||
cleanup_battery();
|
||||
#endif
|
||||
cleanup_separator();
|
||||
cleanup_taskbar();
|
||||
cleanup_panel();
|
||||
cleanup_config();
|
||||
|
||||
if (default_icon) {
|
||||
imlib_context_set_image(default_icon);
|
||||
imlib_free_image();
|
||||
default_icon = NULL;
|
||||
}
|
||||
imlib_context_disconnect_display();
|
||||
|
||||
xsettings_client_destroy(xsettings_client);
|
||||
xsettings_client = NULL;
|
||||
|
||||
cleanup_server();
|
||||
cleanup_timers();
|
||||
|
||||
if (server.display)
|
||||
XCloseDisplay(server.display);
|
||||
server.display = NULL;
|
||||
|
||||
if (sigchild_pipe_valid) {
|
||||
sigchild_pipe_valid = FALSE;
|
||||
close(sigchild_pipe[1]);
|
||||
close(sigchild_pipe[0]);
|
||||
}
|
||||
|
||||
uevent_cleanup();
|
||||
cleanup_fps_distribution();
|
||||
|
||||
#ifdef HAVE_TRACING
|
||||
cleanup_tracing();
|
||||
#endif
|
||||
}
|
||||
7
src/init.h
Normal file
7
src/init.h
Normal file
@@ -0,0 +1,7 @@
|
||||
#ifndef INIT_H
|
||||
#define INIT_H
|
||||
|
||||
void init(int argc, char **argv);
|
||||
void cleanup();
|
||||
|
||||
#endif
|
||||
@@ -60,9 +60,9 @@ void expand_exec(DesktopEntry *entry, const char *path)
|
||||
// %c -> Name
|
||||
// %k -> path
|
||||
if (entry->exec) {
|
||||
char *exec2 = calloc(strlen(entry->exec) + (entry->name ? strlen(entry->name) : 1) +
|
||||
(entry->icon ? strlen(entry->icon) : 1) + 100,
|
||||
1);
|
||||
size_t buf_size = strlen(entry->exec) + (entry->name ? strlen(entry->name) : 1) +
|
||||
(entry->icon ? strlen(entry->icon) : 1) + 100;
|
||||
char *exec2 = calloc(buf_size, 1);
|
||||
char *p, *q;
|
||||
// p will never point to an escaped char
|
||||
for (p = entry->exec, q = exec2; *p; p++, q++) {
|
||||
@@ -82,19 +82,30 @@ void expand_exec(DesktopEntry *entry, const char *path)
|
||||
if (!*p)
|
||||
break;
|
||||
if (*p == 'i' && entry->icon != NULL) {
|
||||
sprintf(q, "--icon '%s'", entry->icon);
|
||||
snprintf(q, buf_size, "--icon '%s'", entry->icon);
|
||||
char *old = q;
|
||||
q += strlen("--icon ''");
|
||||
q += strlen(entry->icon);
|
||||
buf_size -= (size_t)(q - old);
|
||||
q--; // To balance the q++ in the for
|
||||
} else if (*p == 'c' && entry->name != NULL) {
|
||||
sprintf(q, "'%s'", entry->name);
|
||||
snprintf(q, buf_size, "'%s'", entry->name);
|
||||
char *old = q;
|
||||
q += strlen("''");
|
||||
q += strlen(entry->name);
|
||||
buf_size -= (size_t)(q - old);
|
||||
q--; // To balance the q++ in the for
|
||||
} else if (*p == 'c') {
|
||||
sprintf(q, "'%s'", path);
|
||||
snprintf(q, buf_size, "'%s'", path);
|
||||
char *old = q;
|
||||
q += strlen("''");
|
||||
q += strlen(path);
|
||||
buf_size -= (size_t)(q - old);
|
||||
q--; // To balance the q++ in the for
|
||||
} else if (*p == 'f' || *p == 'F') {
|
||||
snprintf(q, buf_size, "%c%c", '%', *p);
|
||||
q += 2;
|
||||
buf_size -= 2;
|
||||
q--; // To balance the q++ in the for
|
||||
} else {
|
||||
// We don't care about other expansions
|
||||
@@ -113,10 +124,12 @@ gboolean read_desktop_file_full_path(const char *path, DesktopEntry *entry)
|
||||
{
|
||||
entry->name = entry->generic_name = entry->icon = entry->exec = entry->cwd = NULL;
|
||||
entry->hidden_from_menus = FALSE;
|
||||
entry->start_in_terminal = FALSE;
|
||||
entry->startup_notification = TRUE;
|
||||
|
||||
FILE *fp = fopen(path, "rt");
|
||||
if (fp == NULL) {
|
||||
fprintf(stderr, "Could not open file %s\n", path);
|
||||
fprintf(stderr, "tint2: Could not open file %s\n", path);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
@@ -126,14 +139,14 @@ gboolean read_desktop_file_full_path(const char *path, DesktopEntry *entry)
|
||||
int lang_index_default = 1;
|
||||
#define LANG_DBG 0
|
||||
if (LANG_DBG)
|
||||
printf("Languages:");
|
||||
fprintf(stderr, "tint2: Languages:");
|
||||
for (int i = 0; languages[i]; i++) {
|
||||
lang_index_default = i + 1;
|
||||
if (LANG_DBG)
|
||||
printf(" %s", languages[i]);
|
||||
fprintf(stderr, "tint2: %s", languages[i]);
|
||||
}
|
||||
if (LANG_DBG)
|
||||
printf("\n");
|
||||
fprintf(stderr, "tint2: \n");
|
||||
// we currently do not know about any Name key at all, so use an invalid index
|
||||
int lang_index_name = lang_index_default + 1;
|
||||
int lang_index_generic_name = lang_index_default + 1;
|
||||
@@ -192,6 +205,10 @@ gboolean read_desktop_file_full_path(const char *path, DesktopEntry *entry)
|
||||
entry->icon = strdup(value);
|
||||
} else if (strcmp(key, "NoDisplay") == 0) {
|
||||
entry->hidden_from_menus = strcasecmp(value, "true") == 0;
|
||||
} else if (strcmp(key, "Terminal") == 0) {
|
||||
entry->start_in_terminal = strcasecmp(value, "true") == 0;
|
||||
} else if (strcmp(key, "StartupNotify") == 0) {
|
||||
entry->startup_notification = strcasecmp(value, "true") == 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -278,11 +295,11 @@ void free_desktop_entry(DesktopEntry *entry)
|
||||
|
||||
void test_read_desktop_file()
|
||||
{
|
||||
fprintf(stdout, "\033[1;33m");
|
||||
fprintf(stderr, YELLOW);
|
||||
DesktopEntry entry;
|
||||
read_desktop_file("/usr/share/applications/firefox.desktop", &entry);
|
||||
printf("Name:%s GenericName:%s Icon:%s Exec:%s\n", entry.name, entry.generic_name, entry.icon, entry.exec);
|
||||
fprintf(stdout, "\033[0m");
|
||||
fprintf(stderr, "tint2: Name:%s GenericName:%s Icon:%s Exec:%s\n", entry.name, entry.generic_name, entry.icon, entry.exec);
|
||||
fprintf(stderr, RESET);
|
||||
}
|
||||
|
||||
GSList *apps_locations = NULL;
|
||||
|
||||
@@ -17,6 +17,8 @@ typedef struct DesktopEntry {
|
||||
char *path;
|
||||
char *cwd;
|
||||
gboolean hidden_from_menus;
|
||||
gboolean start_in_terminal;
|
||||
gboolean startup_notification;
|
||||
} DesktopEntry;
|
||||
|
||||
// Parses a line of the form "key = value". Modifies the line.
|
||||
|
||||
@@ -29,6 +29,8 @@
|
||||
#include "common.h"
|
||||
#include "cache.h"
|
||||
|
||||
gboolean debug_icons = FALSE;
|
||||
|
||||
#define ICON_DIR_TYPE_SCALABLE 0
|
||||
#define ICON_DIR_TYPE_FIXED 1
|
||||
#define ICON_DIR_TYPE_THRESHOLD 2
|
||||
@@ -46,7 +48,7 @@ int parse_theme_line(char *line, char **key, char **value)
|
||||
return parse_dektop_line(line, key, value);
|
||||
}
|
||||
|
||||
GSList *icon_locations = NULL;
|
||||
static GSList *icon_locations = NULL;
|
||||
// Do not free the result.
|
||||
const GSList *get_icon_locations()
|
||||
{
|
||||
@@ -72,6 +74,21 @@ const GSList *get_icon_locations()
|
||||
return icon_locations;
|
||||
}
|
||||
|
||||
static GSList *icon_extensions = NULL;
|
||||
const GSList *get_icon_extensions()
|
||||
{
|
||||
if (icon_extensions)
|
||||
return icon_extensions;
|
||||
|
||||
icon_extensions = g_slist_append(icon_extensions, ".png");
|
||||
icon_extensions = g_slist_append(icon_extensions, ".xpm");
|
||||
#ifdef HAVE_RSVG
|
||||
icon_extensions = g_slist_append(icon_extensions, ".svg");
|
||||
#endif
|
||||
icon_extensions = g_slist_append(icon_extensions, "");
|
||||
return icon_extensions;
|
||||
}
|
||||
|
||||
IconTheme *make_theme(const char *name)
|
||||
{
|
||||
IconTheme *theme = calloc(1, sizeof(IconTheme));
|
||||
@@ -90,7 +107,7 @@ IconTheme *load_theme_from_index(const char *file_name, const char *name)
|
||||
size_t line_size;
|
||||
|
||||
if ((f = fopen(file_name, "rt")) == NULL) {
|
||||
fprintf(stderr, "Could not open theme '%s'\n", file_name);
|
||||
fprintf(stderr, "tint2: Could not open theme '%s'\n", file_name);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
@@ -325,22 +342,22 @@ void free_themes(IconThemeWrapper *wrapper)
|
||||
|
||||
void test_launcher_read_theme_file()
|
||||
{
|
||||
fprintf(stdout, "\033[1;33m");
|
||||
fprintf(stdout, YELLOW);
|
||||
IconTheme *theme = load_theme("oxygen");
|
||||
if (!theme) {
|
||||
printf("Could not load theme\n");
|
||||
fprintf(stderr, "tint2: Could not load theme\n");
|
||||
return;
|
||||
}
|
||||
printf("Loaded theme: %s\n", theme->name);
|
||||
fprintf(stderr, "tint2: Loaded theme: %s\n", theme->name);
|
||||
GSList *item = theme->list_inherits;
|
||||
while (item != NULL) {
|
||||
printf("Inherits:%s\n", (char *)item->data);
|
||||
fprintf(stderr, "tint2: Inherits:%s\n", (char *)item->data);
|
||||
item = g_slist_next(item);
|
||||
}
|
||||
item = theme->list_directories;
|
||||
while (item != NULL) {
|
||||
IconThemeDir *dir = item->data;
|
||||
printf("Dir:%s Size=%d MinSize=%d MaxSize=%d Threshold=%d Type=%s\n",
|
||||
fprintf(stderr, "tint2: Dir:%s Size=%d MinSize=%d MaxSize=%d Threshold=%d Type=%s\n",
|
||||
dir->name,
|
||||
dir->size,
|
||||
dir->min_size,
|
||||
@@ -352,7 +369,7 @@ void test_launcher_read_theme_file()
|
||||
: "?????");
|
||||
item = g_slist_next(item);
|
||||
}
|
||||
fprintf(stdout, "\033[0m");
|
||||
fprintf(stdout, RESET);
|
||||
}
|
||||
|
||||
gboolean str_list_contains(const GSList *list, const char *value)
|
||||
@@ -379,7 +396,7 @@ void load_themes_helper(const char *name, GSList **themes, GSList **queued)
|
||||
char *queued_name = queue->data;
|
||||
queue = g_slist_remove(queue, queued_name);
|
||||
|
||||
fprintf(stderr, " '%s',", queued_name);
|
||||
fprintf(stderr, "tint2: '%s',", queued_name);
|
||||
IconTheme *theme = load_theme(queued_name);
|
||||
if (theme != NULL) {
|
||||
*themes = g_slist_append(*themes, theme);
|
||||
@@ -399,7 +416,7 @@ void load_themes_helper(const char *name, GSList **themes, GSList **queued)
|
||||
|
||||
free(queued_name);
|
||||
}
|
||||
fprintf(stderr, "\n");
|
||||
fprintf(stderr, "tint2: \n");
|
||||
|
||||
// Free the queue
|
||||
GSList *l;
|
||||
@@ -413,7 +430,7 @@ void load_default_theme(IconThemeWrapper *wrapper)
|
||||
if (wrapper->_themes_loaded)
|
||||
return;
|
||||
|
||||
fprintf(stderr, GREEN "Loading icon theme %s:" RESET "\n", wrapper->icon_theme_name);
|
||||
fprintf(stderr, GREEN "tint2: Loading icon theme %s:" RESET "\n", wrapper->icon_theme_name);
|
||||
|
||||
load_themes_helper(wrapper->icon_theme_name, &wrapper->themes, &wrapper->_queued);
|
||||
load_themes_helper("hicolor", &wrapper->themes, &wrapper->_queued);
|
||||
@@ -426,7 +443,7 @@ void load_fallbacks(IconThemeWrapper *wrapper)
|
||||
if (wrapper->_fallback_loaded)
|
||||
return;
|
||||
|
||||
fprintf(stderr, RED "Loading additional icon themes (this means your icon theme is incomplete)..." RESET "\n");
|
||||
fprintf(stderr, RED "tint2: Loading additional icon themes (this means your icon theme is incomplete)..." RESET "\n");
|
||||
|
||||
// Load wrapper->themes_fallback
|
||||
const GSList *location;
|
||||
@@ -459,7 +476,7 @@ void load_icon_cache(IconThemeWrapper *wrapper)
|
||||
if (wrapper->_cache.loaded)
|
||||
return;
|
||||
|
||||
fprintf(stderr, GREEN "Loading icon theme cache..." RESET "\n");
|
||||
fprintf(stderr, GREEN "tint2: Loading icon theme cache..." RESET "\n");
|
||||
|
||||
gchar *cache_path = get_icon_cache_path();
|
||||
load_cache(&wrapper->_cache, cache_path);
|
||||
@@ -471,7 +488,7 @@ void save_icon_cache(IconThemeWrapper *wrapper)
|
||||
if (!wrapper || !wrapper->_cache.dirty)
|
||||
return;
|
||||
|
||||
fprintf(stderr, GREEN "Saving icon theme cache..." RESET "\n");
|
||||
fprintf(stderr, GREEN "tint2: Saving icon theme cache..." RESET "\n");
|
||||
gchar *cache_path = get_icon_cache_path();
|
||||
save_cache(&wrapper->_cache, cache_path);
|
||||
g_free(cache_path);
|
||||
@@ -482,7 +499,7 @@ IconThemeWrapper *load_themes(const char *icon_theme_name)
|
||||
IconThemeWrapper *wrapper = calloc(1, sizeof(IconThemeWrapper));
|
||||
|
||||
if (!icon_theme_name) {
|
||||
fprintf(stderr, "Missing icon_theme_name theme, default to 'hicolor'.\n");
|
||||
fprintf(stderr, "tint2: Missing icon_theme_name theme, default to 'hicolor'.\n");
|
||||
icon_theme_name = "hicolor";
|
||||
}
|
||||
|
||||
@@ -533,42 +550,42 @@ gint compare_theme_directories(gconstpointer a, gconstpointer b, gpointer size_q
|
||||
return abs(da->size - size) - abs(db->size - size);
|
||||
}
|
||||
|
||||
#define DEBUG_ICON_SEARCH 0
|
||||
Bool is_full_path(const char *s)
|
||||
{
|
||||
if (!s)
|
||||
return FALSE;
|
||||
return s[0] == '/';
|
||||
}
|
||||
|
||||
Bool file_exists(const char *path)
|
||||
{
|
||||
return g_file_test(path, G_FILE_TEST_EXISTS);
|
||||
}
|
||||
|
||||
char *icon_path_from_full_path(const char *s)
|
||||
{
|
||||
if (is_full_path(s) && file_exists(s))
|
||||
return strdup(s);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
char *get_icon_path_helper(GSList *themes, const char *icon_name, int size)
|
||||
{
|
||||
if (icon_name == NULL)
|
||||
if (!icon_name)
|
||||
return NULL;
|
||||
|
||||
// If the icon_name is already a path and the file exists, return it
|
||||
if (strstr(icon_name, "/") == icon_name) {
|
||||
if (g_file_test(icon_name, G_FILE_TEST_EXISTS))
|
||||
return strdup(icon_name);
|
||||
else
|
||||
return NULL;
|
||||
}
|
||||
char *result = icon_path_from_full_path(icon_name);
|
||||
if (result)
|
||||
return result;
|
||||
|
||||
const GSList *basenames = get_icon_locations();
|
||||
GSList *extensions = NULL;
|
||||
extensions = g_slist_append(extensions, ".png");
|
||||
extensions = g_slist_append(extensions, ".xpm");
|
||||
#ifdef HAVE_RSVG
|
||||
extensions = g_slist_append(extensions, ".svg");
|
||||
#endif
|
||||
// if the icon name already contains one of the extensions (e.g. vlc.png instead of vlc) add a special entry
|
||||
for (GSList *ext = extensions; ext; ext = g_slist_next(ext)) {
|
||||
char *extension = (char *)ext->data;
|
||||
if (strlen(icon_name) > strlen(extension) &&
|
||||
strcmp(extension, icon_name + strlen(icon_name) - strlen(extension)) == 0) {
|
||||
extensions = g_slist_append(extensions, "");
|
||||
break;
|
||||
}
|
||||
}
|
||||
const GSList *extensions = get_icon_extensions();
|
||||
|
||||
GSList *theme;
|
||||
|
||||
// Best size match
|
||||
// Contrary to the freedesktop spec, we are not choosing the closest icon in size, but the next larger icon
|
||||
// otherwise the quality is usually crap (for size 22, if you can choose 16 or 32, you're better with 32)
|
||||
// otherwise the quality is worse when scaling up (for size 22, if you can choose 16 or 32, you're better with 32)
|
||||
// We do fallback to the closest size if we cannot find a larger or equal icon
|
||||
|
||||
// These 3 variables are used for keeping the closest size match
|
||||
@@ -581,10 +598,12 @@ char *get_icon_path_helper(GSList *themes, const char *icon_name, int size)
|
||||
char *next_larger = NULL;
|
||||
GSList *next_larger_theme = NULL;
|
||||
|
||||
int file_name_size = 4096;
|
||||
size_t file_name_size = 4096;
|
||||
char *file_name = calloc(file_name_size, 1);
|
||||
|
||||
for (theme = themes; theme; theme = g_slist_next(theme)) {
|
||||
if (debug_icons)
|
||||
fprintf(stderr, "tint2: Searching theme: %s\n", ((IconTheme *)theme->data)->name);
|
||||
((IconTheme *)theme->data)->list_directories =
|
||||
g_slist_sort_with_data(((IconTheme *)theme->data)->list_directories,
|
||||
compare_theme_directories,
|
||||
@@ -600,9 +619,11 @@ char *get_icon_path_helper(GSList *themes, const char *icon_name, int size)
|
||||
(!next_larger_theme ? 1 : theme == next_larger_theme));
|
||||
if (!possible)
|
||||
continue;
|
||||
if (debug_icons)
|
||||
fprintf(stderr, "tint2: Searching directory: %s\n", ((IconThemeDir *)dir->data)->name);
|
||||
const GSList *base;
|
||||
for (base = basenames; base; base = g_slist_next(base)) {
|
||||
for (GSList *ext = extensions; ext; ext = g_slist_next(ext)) {
|
||||
for (const GSList *ext = extensions; ext; ext = g_slist_next(ext)) {
|
||||
char *base_name = (char *)base->data;
|
||||
char *theme_name = ((IconTheme *)theme->data)->name;
|
||||
char *dir_name = ((IconThemeDir *)dir->data)->name;
|
||||
@@ -616,12 +637,12 @@ char *get_icon_path_helper(GSList *themes, const char *icon_name, int size)
|
||||
}
|
||||
file_name[0] = 0;
|
||||
// filename = directory/$(themename)/subdirectory/iconname.extension
|
||||
sprintf(file_name, "%s/%s/%s/%s%s", base_name, theme_name, dir_name, icon_name, extension);
|
||||
if (DEBUG_ICON_SEARCH)
|
||||
printf("checking %s\n", file_name);
|
||||
snprintf(file_name, (size_t)file_name_size, "%s/%s/%s/%s%s", base_name, theme_name, dir_name, icon_name, extension);
|
||||
if (debug_icons)
|
||||
fprintf(stderr, "tint2: Checking %s\n", file_name);
|
||||
if (g_file_test(file_name, G_FILE_TEST_EXISTS)) {
|
||||
if (DEBUG_ICON_SEARCH)
|
||||
printf("found: %s\n", file_name);
|
||||
if (debug_icons)
|
||||
fprintf(stderr, "tint2: Found potential match: %s\n", file_name);
|
||||
// Closest match
|
||||
if (directory_size_distance((IconThemeDir *)dir->data, size) < minimal_size &&
|
||||
(!best_file_theme ? 1 : theme == best_file_theme)) {
|
||||
@@ -632,8 +653,8 @@ char *get_icon_path_helper(GSList *themes, const char *icon_name, int size)
|
||||
best_file_name = strdup(file_name);
|
||||
minimal_size = directory_size_distance((IconThemeDir *)dir->data, size);
|
||||
best_file_theme = theme;
|
||||
if (DEBUG_ICON_SEARCH)
|
||||
printf("best_file_name = %s; minimal_size = %d\n", best_file_name, minimal_size);
|
||||
if (debug_icons)
|
||||
fprintf(stderr, "tint2: best_file_name = %s; minimal_size = %d\n", best_file_name, minimal_size);
|
||||
}
|
||||
// Next larger match
|
||||
if (((IconThemeDir *)dir->data)->size >= size &&
|
||||
@@ -646,8 +667,8 @@ char *get_icon_path_helper(GSList *themes, const char *icon_name, int size)
|
||||
next_larger = strdup(file_name);
|
||||
next_larger_size = ((IconThemeDir *)dir->data)->size;
|
||||
next_larger_theme = theme;
|
||||
if (DEBUG_ICON_SEARCH)
|
||||
printf("next_larger = %s; next_larger_size = %d\n", next_larger, next_larger_size);
|
||||
if (debug_icons)
|
||||
fprintf(stderr, "tint2: next_larger = %s; next_larger_size = %d\n", next_larger, next_larger_size);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -657,28 +678,30 @@ char *get_icon_path_helper(GSList *themes, const char *icon_name, int size)
|
||||
free(file_name);
|
||||
file_name = NULL;
|
||||
if (next_larger) {
|
||||
g_slist_free(extensions);
|
||||
free(best_file_name);
|
||||
return next_larger;
|
||||
}
|
||||
if (best_file_name) {
|
||||
g_slist_free(extensions);
|
||||
return best_file_name;
|
||||
}
|
||||
|
||||
// Look in unthemed icons
|
||||
{
|
||||
if (debug_icons)
|
||||
fprintf(stderr, "tint2: Searching unthemed icons\n");
|
||||
for (const GSList *base = basenames; base; base = g_slist_next(base)) {
|
||||
for (GSList *ext = extensions; ext; ext = g_slist_next(ext)) {
|
||||
for (const GSList *ext = extensions; ext; ext = g_slist_next(ext)) {
|
||||
char *base_name = (char *)base->data;
|
||||
char *extension = (char *)ext->data;
|
||||
file_name = calloc(strlen(base_name) + strlen(icon_name) + strlen(extension) + 100, 1);
|
||||
size_t file_name_size2 = strlen(base_name) + strlen(icon_name) + strlen(extension) + 100;
|
||||
file_name = calloc(file_name_size2, 1);
|
||||
// filename = directory/iconname.extension
|
||||
sprintf(file_name, "%s/%s%s", base_name, icon_name, extension);
|
||||
if (DEBUG_ICON_SEARCH)
|
||||
printf("checking %s\n", file_name);
|
||||
snprintf(file_name, file_name_size2, "%s/%s%s", base_name, icon_name, extension);
|
||||
if (debug_icons)
|
||||
fprintf(stderr, "tint2: Checking %s\n", file_name);
|
||||
if (g_file_test(file_name, G_FILE_TEST_EXISTS)) {
|
||||
g_slist_free(extensions);
|
||||
if (debug_icons)
|
||||
fprintf(stderr, "tint2: Found %s\n", file_name);
|
||||
return file_name;
|
||||
} else {
|
||||
free(file_name);
|
||||
@@ -688,7 +711,6 @@ char *get_icon_path_helper(GSList *themes, const char *icon_name, int size)
|
||||
}
|
||||
}
|
||||
|
||||
g_slist_free(extensions);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
@@ -715,7 +737,7 @@ char *get_icon_path_from_cache(IconThemeWrapper *wrapper, const char *icon_name,
|
||||
if (!g_file_test(value, G_FILE_TEST_EXISTS))
|
||||
return NULL;
|
||||
|
||||
// fprintf(stderr, "Icon path found in cache: theme = %s, icon = %s, size = %d, path = %s\n",
|
||||
// fprintf(stderr, "tint2: Icon path found in cache: theme = %s, icon = %s, size = %d, path = %s\n",
|
||||
// wrapper->icon_theme_name, icon_name, size, value);
|
||||
|
||||
return strdup(value);
|
||||
@@ -742,28 +764,44 @@ void add_icon_path_to_cache(IconThemeWrapper *wrapper, const char *icon_name, in
|
||||
|
||||
char *get_icon_path(IconThemeWrapper *wrapper, const char *icon_name, int size, gboolean use_fallbacks)
|
||||
{
|
||||
if (!wrapper)
|
||||
if (debug_icons)
|
||||
fprintf(stderr,
|
||||
"Searching for icon %s with size %d, fallbacks %sallowed\n",
|
||||
icon_name,
|
||||
size,
|
||||
use_fallbacks ? "" : "not ");
|
||||
if (!wrapper) {
|
||||
if (debug_icons)
|
||||
fprintf(stderr,
|
||||
"Icon search aborted, themes not loaded\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (!icon_name || strlen(icon_name) == 0)
|
||||
goto notfound;
|
||||
|
||||
char *path = get_icon_path_from_cache(wrapper, icon_name, size);
|
||||
if (path)
|
||||
if (path) {
|
||||
if (debug_icons)
|
||||
fprintf(stderr,
|
||||
"Icon found in cache: %s\n", path);
|
||||
return path;
|
||||
}
|
||||
|
||||
load_default_theme(wrapper);
|
||||
|
||||
icon_name = icon_name ? icon_name : DEFAULT_ICON;
|
||||
path = get_icon_path_helper(wrapper->themes, icon_name, size);
|
||||
if (path) {
|
||||
if (debug_icons)
|
||||
fprintf(stderr, "tint2: Icon found: %s\n", path);
|
||||
add_icon_path_to_cache(wrapper, icon_name, size, path);
|
||||
return path;
|
||||
}
|
||||
|
||||
if (!use_fallbacks)
|
||||
goto notfound;
|
||||
fprintf(stderr, YELLOW "Icon not found in default theme: %s" RESET "\n", icon_name);
|
||||
fprintf(stderr, YELLOW "tint2: Icon not found in default theme: %s" RESET "\n", icon_name);
|
||||
load_fallbacks(wrapper);
|
||||
|
||||
path = get_icon_path_helper(wrapper->themes_fallback, icon_name, size);
|
||||
@@ -773,7 +811,7 @@ char *get_icon_path(IconThemeWrapper *wrapper, const char *icon_name, int size,
|
||||
}
|
||||
|
||||
notfound:
|
||||
fprintf(stderr, RED "Could not find icon '%s', using default." RESET "\n", icon_name);
|
||||
fprintf(stderr, RED "tint2: Could not find icon '%s', using default." RESET "\n", icon_name);
|
||||
path = get_icon_path_helper(wrapper->themes, DEFAULT_ICON, size);
|
||||
if (path)
|
||||
return path;
|
||||
|
||||
@@ -57,4 +57,6 @@ char *get_icon_path(IconThemeWrapper *wrapper, const char *icon_name, int size,
|
||||
// Do not free the result, it is cached.
|
||||
const GSList *get_icon_locations();
|
||||
|
||||
extern gboolean debug_icons;
|
||||
|
||||
#endif
|
||||
|
||||
@@ -64,6 +64,8 @@ void launcher_reload_hidden_icons(Launcher *launcher);
|
||||
void launcher_icon_on_change_layout(void *obj);
|
||||
int launcher_compute_desired_size(void *obj);
|
||||
|
||||
void relayout_launcher();
|
||||
|
||||
void default_launcher()
|
||||
{
|
||||
launcher_enabled = 0;
|
||||
@@ -95,6 +97,7 @@ void init_launcher_panel(void *p)
|
||||
launcher->area._draw_foreground = NULL;
|
||||
launcher->area.size_mode = LAYOUT_FIXED;
|
||||
launcher->area._resize = resize_launcher;
|
||||
launcher->area._on_change_layout = relayout_launcher;
|
||||
launcher->area._compute_desired_size = launcher_compute_desired_size;
|
||||
launcher->area.resize_needed = 1;
|
||||
schedule_redraw(&launcher->area);
|
||||
@@ -294,7 +297,7 @@ gboolean resize_launcher(void *obj)
|
||||
launcherIcon->y = posy;
|
||||
launcherIcon->x = posx;
|
||||
launcher_icon_on_change_layout(launcherIcon);
|
||||
// printf("launcher %d : %d,%d\n", i, posx, posy);
|
||||
// fprintf(stderr, "tint2: launcher %d : %d,%d\n", i, posx, posy);
|
||||
if (panel_horizontal) {
|
||||
if (i % icons_per_column) {
|
||||
posy += launcher->icon_size + launcher->area.paddingx;
|
||||
@@ -325,6 +328,17 @@ gboolean resize_launcher(void *obj)
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
void relayout_launcher(void *obj)
|
||||
{
|
||||
Launcher *launcher = (Launcher *)obj;
|
||||
for (GSList *l = launcher->list_icons; l; l = l->next) {
|
||||
LauncherIcon *launcherIcon = (LauncherIcon *)l->data;
|
||||
if (!launcherIcon->area.on_screen)
|
||||
continue;
|
||||
launcher_icon_on_change_layout(launcherIcon);
|
||||
}
|
||||
}
|
||||
|
||||
// Here we override the default layout of the icons; normally Area layouts its children
|
||||
// in a stack; we need to layout them in a kind of table
|
||||
void launcher_icon_on_change_layout(void *obj)
|
||||
@@ -371,7 +385,12 @@ void draw_launcher_icon(void *obj, cairo_t *c)
|
||||
void launcher_icon_dump_geometry(void *obj, int indent)
|
||||
{
|
||||
LauncherIcon *launcherIcon = (LauncherIcon *)obj;
|
||||
fprintf(stderr, "%*sIcon: w = h = %d, name = %s\n", indent, "", launcherIcon->icon_size, launcherIcon->icon_name);
|
||||
fprintf(stderr,
|
||||
"tint2: %*sIcon: w = h = %d, name = %s\n",
|
||||
indent,
|
||||
"",
|
||||
launcherIcon->icon_size,
|
||||
launcherIcon->icon_name);
|
||||
}
|
||||
|
||||
Imlib_Image scale_icon(Imlib_Image original, int icon_size)
|
||||
@@ -420,8 +439,21 @@ void launcher_action(LauncherIcon *icon, XEvent *evt, int x, int y)
|
||||
launcher_reload_icon((Launcher *)icon->area.parent, icon);
|
||||
launcher_reload_hidden_icons((Launcher *)icon->area.parent);
|
||||
|
||||
if (evt->type == ButtonPress || evt->type == ButtonRelease)
|
||||
tint_exec(icon->cmd, icon->cwd, icon->icon_tooltip, evt->xbutton.time, &icon->area, x, y);
|
||||
if (evt->type == ButtonPress || evt->type == ButtonRelease) {
|
||||
GString *cmd = g_string_new(icon->cmd);
|
||||
tint2_g_string_replace(cmd, "%f", "");
|
||||
tint2_g_string_replace(cmd, "%F", "");
|
||||
tint_exec(cmd->str,
|
||||
icon->cwd,
|
||||
icon->icon_tooltip,
|
||||
evt->xbutton.time,
|
||||
&icon->area,
|
||||
x,
|
||||
y,
|
||||
icon->start_in_terminal,
|
||||
icon->startup_notification);
|
||||
g_string_free(cmd, TRUE);
|
||||
}
|
||||
}
|
||||
|
||||
// Populates the list_icons list from the list_apps list
|
||||
@@ -438,7 +470,7 @@ void launcher_load_icons(Launcher *launcher)
|
||||
launcherIcon->area.size_mode = LAYOUT_FIXED;
|
||||
launcherIcon->area._resize = NULL;
|
||||
launcherIcon->area._compute_desired_size = launcher_icon_compute_desired_size;
|
||||
sprintf(launcherIcon->area.name, "LauncherIcon %d", index);
|
||||
snprintf(launcherIcon->area.name, sizeof(launcherIcon->area.name), "LauncherIcon %d", index);
|
||||
launcherIcon->area.resize_needed = 0;
|
||||
launcherIcon->area.has_mouse_over_effect = panel_config.mouse_effects;
|
||||
launcherIcon->area.has_mouse_press_effect = launcherIcon->area.has_mouse_over_effect;
|
||||
@@ -476,6 +508,8 @@ void launcher_reload_icon(Launcher *launcher, LauncherIcon *launcherIcon)
|
||||
launcherIcon->cwd = strdup(entry.cwd);
|
||||
else
|
||||
launcherIcon->cwd = NULL;
|
||||
launcherIcon->start_in_terminal = entry.start_in_terminal;
|
||||
launcherIcon->startup_notification = entry.startup_notification;
|
||||
if (launcherIcon->icon_name)
|
||||
free(launcherIcon->icon_name);
|
||||
launcherIcon->icon_name = entry.icon ? strdup(entry.icon) : strdup(DEFAULT_ICON);
|
||||
@@ -531,7 +565,7 @@ void launcher_reload_icon_image(Launcher *launcher, LauncherIcon *launcherIcon)
|
||||
free_icon(original);
|
||||
free(launcherIcon->icon_path);
|
||||
launcherIcon->icon_path = new_icon_path;
|
||||
// fprintf(stderr, "launcher.c %d: Using icon %s\n", __LINE__, launcherIcon->icon_path);
|
||||
// fprintf(stderr, "tint2: launcher.c %d: Using icon %s\n", __LINE__, launcherIcon->icon_path);
|
||||
|
||||
if (panel_config.mouse_effects) {
|
||||
launcherIcon->image_hover = adjust_icon(launcherIcon->image,
|
||||
|
||||
@@ -33,6 +33,8 @@ typedef struct LauncherIcon {
|
||||
Imlib_Image image_pressed;
|
||||
char *cmd;
|
||||
char *cwd;
|
||||
gboolean start_in_terminal;
|
||||
gboolean startup_notification;
|
||||
char *icon_name;
|
||||
char *icon_path;
|
||||
char *icon_tooltip;
|
||||
|
||||
@@ -48,7 +48,7 @@ void xsettings_notify_cb(const char *name, XSettingsAction action, XSettingsSett
|
||||
{
|
||||
if ((action == XSETTINGS_ACTION_NEW || action == XSETTINGS_ACTION_CHANGED) && name != NULL && setting != NULL) {
|
||||
if (strcmp(name, "Net/IconThemeName") == 0 && setting->type == XSETTINGS_TYPE_STRING) {
|
||||
fprintf(stderr, "xsettings: %s = %s\n", name, setting->data.v_string);
|
||||
fprintf(stderr, "tint2: xsettings: %s = %s\n", name, setting->data.v_string);
|
||||
if (icon_theme_name_xsettings) {
|
||||
if (strcmp(icon_theme_name_xsettings, setting->data.v_string) == 0)
|
||||
return;
|
||||
@@ -57,7 +57,7 @@ void xsettings_notify_cb(const char *name, XSettingsAction action, XSettingsSett
|
||||
icon_theme_name_xsettings = strdup(setting->data.v_string);
|
||||
default_icon_theme_changed();
|
||||
} else if (strcmp(name, "Gtk/FontName") == 0 && setting->type == XSETTINGS_TYPE_STRING) {
|
||||
fprintf(stderr, "xsettings: %s = %s\n", name, setting->data.v_string);
|
||||
fprintf(stderr, "tint2: xsettings: %s = %s\n", name, setting->data.v_string);
|
||||
if (default_font) {
|
||||
if (strcmp(default_font, setting->data.v_string) == 0)
|
||||
return;
|
||||
@@ -191,7 +191,7 @@ static XSettingsList *parse_settings(unsigned char *data, size_t len)
|
||||
|
||||
result = fetch_card8(&buffer, (CARD8 *)&buffer.byte_order);
|
||||
if (buffer.byte_order != MSBFirst && buffer.byte_order != LSBFirst) {
|
||||
fprintf(stderr, "Invalid byte order %x in XSETTINGS property\n", buffer.byte_order);
|
||||
fprintf(stderr, "tint2: Invalid byte order %x in XSETTINGS property\n", buffer.byte_order);
|
||||
result = XSETTINGS_FAILED;
|
||||
goto out;
|
||||
}
|
||||
@@ -312,13 +312,13 @@ out:
|
||||
if (result != XSETTINGS_SUCCESS) {
|
||||
switch (result) {
|
||||
case XSETTINGS_NO_MEM:
|
||||
fprintf(stderr, "Out of memory reading XSETTINGS property\n");
|
||||
fprintf(stderr, "tint2: Out of memory reading XSETTINGS property\n");
|
||||
break;
|
||||
case XSETTINGS_ACCESS:
|
||||
fprintf(stderr, "Invalid XSETTINGS property (read off end)\n");
|
||||
fprintf(stderr, "tint2: Invalid XSETTINGS property (read off end)\n");
|
||||
break;
|
||||
case XSETTINGS_DUPLICATE_ENTRY:
|
||||
fprintf(stderr, "Duplicate XSETTINGS entry for '%s'\n", setting->name);
|
||||
fprintf(stderr, "tint2: Duplicate XSETTINGS entry for '%s'\n", setting->name);
|
||||
case XSETTINGS_FAILED:
|
||||
case XSETTINGS_SUCCESS:
|
||||
case XSETTINGS_NO_ENTRY:
|
||||
@@ -365,7 +365,7 @@ static void read_settings(XSettingsClient *client)
|
||||
|
||||
if (result == Success && type == server.atom._XSETTINGS_SETTINGS) {
|
||||
if (format != 8) {
|
||||
fprintf(stderr, "Invalid format for XSETTINGS property %d", format);
|
||||
fprintf(stderr, "tint2: Invalid format for XSETTINGS property %d", format);
|
||||
} else
|
||||
client->settings = parse_settings(data, n_items);
|
||||
XFree(data);
|
||||
@@ -420,7 +420,7 @@ XSettingsClient *xsettings_client_new(Display *display,
|
||||
check_manager_window(client);
|
||||
|
||||
if (client->manager_window == None) {
|
||||
printf("No XSETTINGS manager, tint2 uses config option 'launcher_icon_theme'.\n");
|
||||
fprintf(stderr, "tint2: No XSETTINGS manager, tint2 uses config option 'launcher_icon_theme'.\n");
|
||||
free(client);
|
||||
return NULL;
|
||||
} else {
|
||||
|
||||
806
src/main.c
Normal file
806
src/main.c
Normal file
@@ -0,0 +1,806 @@
|
||||
/**************************************************************************
|
||||
*
|
||||
* Tint2 panel
|
||||
*
|
||||
* Copyright (C) 2007 Pål Staurland (staura@gmail.com)
|
||||
* Modified (C) 2008 thierry lorthiois (lorthiois@bbsoft.fr) from Omega distribution
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License version 2
|
||||
* as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
**************************************************************************/
|
||||
|
||||
#include <errno.h>
|
||||
#include <sys/stat.h>
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
#include <X11/Xutil.h>
|
||||
#include <X11/Xatom.h>
|
||||
#include <X11/Xlocale.h>
|
||||
#include <X11/extensions/Xdamage.h>
|
||||
#include <Imlib2.h>
|
||||
#include <signal.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/types.h>
|
||||
#include <pwd.h>
|
||||
#include <time.h>
|
||||
#include <sys/time.h>
|
||||
#include <sys/wait.h>
|
||||
|
||||
#ifdef HAVE_SN
|
||||
#include <libsn/sn.h>
|
||||
#endif
|
||||
|
||||
#include "config.h"
|
||||
#include "drag_and_drop.h"
|
||||
#include "fps_distribution.h"
|
||||
#include "init.h"
|
||||
#include "launcher.h"
|
||||
#include "mouse_actions.h"
|
||||
#include "panel.h"
|
||||
#include "server.h"
|
||||
#include "signals.h"
|
||||
#include "systraybar.h"
|
||||
#include "task.h"
|
||||
#include "taskbar.h"
|
||||
#include "tooltip.h"
|
||||
#include "timer.h"
|
||||
#include "tracing.h"
|
||||
#include "uevent.h"
|
||||
#include "version.h"
|
||||
#include "window.h"
|
||||
#include "xsettings-client.h"
|
||||
|
||||
// Global process state variables
|
||||
|
||||
XSettingsClient *xsettings_client = NULL;
|
||||
|
||||
gboolean debug_fps = FALSE;
|
||||
gboolean debug_frames = FALSE;
|
||||
static int frame = 0;
|
||||
double tracing_fps_threshold = 60;
|
||||
static double ts_event_read;
|
||||
static double ts_event_processed;
|
||||
static double ts_render_finished;
|
||||
static double ts_flush_finished;
|
||||
|
||||
static gboolean first_render;
|
||||
|
||||
void handle_event_property_notify(XEvent *e)
|
||||
{
|
||||
gboolean debug = FALSE;
|
||||
|
||||
Window win = e->xproperty.window;
|
||||
Atom at = e->xproperty.atom;
|
||||
|
||||
if (xsettings_client)
|
||||
xsettings_client_process_event(xsettings_client, e);
|
||||
for (int i = 0; i < num_panels; i++) {
|
||||
Panel *p = &panels[i];
|
||||
if (win == p->main_win) {
|
||||
if (at == server.atom._NET_WM_DESKTOP && get_window_desktop(p->main_win) != ALL_DESKTOPS)
|
||||
replace_panel_all_desktops(p);
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (win == server.root_win) {
|
||||
if (!server.got_root_win) {
|
||||
XSelectInput(server.display, server.root_win, PropertyChangeMask | StructureNotifyMask);
|
||||
server.got_root_win = TRUE;
|
||||
}
|
||||
|
||||
// Change name of desktops
|
||||
else if (at == server.atom._NET_DESKTOP_NAMES) {
|
||||
if (debug)
|
||||
fprintf(stderr, "tint2: %s %d: win = root, atom = _NET_DESKTOP_NAMES\n", __func__, __LINE__);
|
||||
update_desktop_names();
|
||||
}
|
||||
// Change desktops
|
||||
else if (at == server.atom._NET_NUMBER_OF_DESKTOPS || at == server.atom._NET_DESKTOP_GEOMETRY ||
|
||||
at == server.atom._NET_DESKTOP_VIEWPORT || at == server.atom._NET_WORKAREA ||
|
||||
at == server.atom._NET_CURRENT_DESKTOP) {
|
||||
if (debug)
|
||||
fprintf(stderr, "tint2: %s %d: win = root, atom = ?? desktops changed\n", __func__, __LINE__);
|
||||
if (!taskbar_enabled)
|
||||
return;
|
||||
int old_num_desktops = server.num_desktops;
|
||||
int old_desktop = server.desktop;
|
||||
server_get_number_of_desktops();
|
||||
server.desktop = get_current_desktop();
|
||||
if (old_num_desktops != server.num_desktops) { // If number of desktop changed
|
||||
if (server.num_desktops <= server.desktop) {
|
||||
server.desktop = server.num_desktops - 1;
|
||||
}
|
||||
cleanup_taskbar();
|
||||
init_taskbar();
|
||||
for (int i = 0; i < num_panels; i++) {
|
||||
init_taskbar_panel(&panels[i]);
|
||||
set_panel_items_order(&panels[i]);
|
||||
panels[i].area.resize_needed = 1;
|
||||
}
|
||||
taskbar_refresh_tasklist();
|
||||
reset_active_task();
|
||||
update_all_taskbars_visibility();
|
||||
if (old_desktop != server.desktop)
|
||||
tooltip_trigger_hide();
|
||||
schedule_panel_redraw();
|
||||
} else if (old_desktop != server.desktop) {
|
||||
tooltip_trigger_hide();
|
||||
for (int i = 0; i < num_panels; i++) {
|
||||
Panel *panel = &panels[i];
|
||||
set_taskbar_state(&panel->taskbar[old_desktop], TASKBAR_NORMAL);
|
||||
set_taskbar_state(&panel->taskbar[server.desktop], TASKBAR_ACTIVE);
|
||||
// check ALL_DESKTOPS task => resize taskbar
|
||||
Taskbar *taskbar;
|
||||
if (server.num_desktops > old_desktop) {
|
||||
taskbar = &panel->taskbar[old_desktop];
|
||||
GList *l = taskbar->area.children;
|
||||
if (taskbarname_enabled)
|
||||
l = l->next;
|
||||
for (; l; l = l->next) {
|
||||
Task *task = l->data;
|
||||
if (task->desktop == ALL_DESKTOPS) {
|
||||
task->area.on_screen = always_show_all_desktop_tasks;
|
||||
taskbar->area.resize_needed = 1;
|
||||
schedule_panel_redraw();
|
||||
if (taskbar_mode == MULTI_DESKTOP)
|
||||
panel->area.resize_needed = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
taskbar = &panel->taskbar[server.desktop];
|
||||
GList *l = taskbar->area.children;
|
||||
if (taskbarname_enabled)
|
||||
l = l->next;
|
||||
for (; l; l = l->next) {
|
||||
Task *task = l->data;
|
||||
if (task->desktop == ALL_DESKTOPS) {
|
||||
task->area.on_screen = TRUE;
|
||||
taskbar->area.resize_needed = 1;
|
||||
if (taskbar_mode == MULTI_DESKTOP)
|
||||
panel->area.resize_needed = 1;
|
||||
}
|
||||
}
|
||||
|
||||
if (server.viewports) {
|
||||
GList *need_update = NULL;
|
||||
|
||||
GHashTableIter iter;
|
||||
gpointer key, value;
|
||||
|
||||
g_hash_table_iter_init(&iter, win_to_task);
|
||||
while (g_hash_table_iter_next(&iter, &key, &value)) {
|
||||
Window task_win = *(Window *)key;
|
||||
Task *task = get_task(task_win);
|
||||
if (task) {
|
||||
int desktop = get_window_desktop(task_win);
|
||||
if (desktop != task->desktop) {
|
||||
need_update = g_list_append(need_update, task);
|
||||
}
|
||||
}
|
||||
}
|
||||
for (l = need_update; l; l = l->next) {
|
||||
Task *task = l->data;
|
||||
task_update_desktop(task);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// Window list
|
||||
else if (at == server.atom._NET_CLIENT_LIST) {
|
||||
if (debug)
|
||||
fprintf(stderr, "tint2: %s %d: win = root, atom = _NET_CLIENT_LIST\n", __func__, __LINE__);
|
||||
taskbar_refresh_tasklist();
|
||||
update_all_taskbars_visibility();
|
||||
schedule_panel_redraw();
|
||||
}
|
||||
// Change active
|
||||
else if (at == server.atom._NET_ACTIVE_WINDOW) {
|
||||
if (debug)
|
||||
fprintf(stderr, "tint2: %s %d: win = root, atom = _NET_ACTIVE_WINDOW\n", __func__, __LINE__);
|
||||
reset_active_task();
|
||||
schedule_panel_redraw();
|
||||
} else if (at == server.atom._XROOTPMAP_ID || at == server.atom._XROOTMAP_ID) {
|
||||
if (debug)
|
||||
fprintf(stderr, "tint2: %s %d: win = root, atom = _XROOTPMAP_ID\n", __func__, __LINE__);
|
||||
// change Wallpaper
|
||||
for (int i = 0; i < num_panels; i++) {
|
||||
set_panel_background(&panels[i]);
|
||||
}
|
||||
schedule_panel_redraw();
|
||||
}
|
||||
} else {
|
||||
TrayWindow *traywin = systray_find_icon(win);
|
||||
if (traywin) {
|
||||
systray_property_notify(traywin, e);
|
||||
return;
|
||||
}
|
||||
|
||||
Task *task = get_task(win);
|
||||
if (debug) {
|
||||
char *atom_name = XGetAtomName(server.display, at);
|
||||
fprintf(stderr,
|
||||
"%s %d: win = %ld, task = %s, atom = %s\n",
|
||||
__func__,
|
||||
__LINE__,
|
||||
win,
|
||||
task ? (task->title ? task->title : "??") : "null",
|
||||
atom_name);
|
||||
XFree(atom_name);
|
||||
}
|
||||
if (!task) {
|
||||
if (debug)
|
||||
fprintf(stderr, "tint2: %s %d\n", __func__, __LINE__);
|
||||
if (at == server.atom._NET_WM_STATE) {
|
||||
// xfce4 sends _NET_WM_STATE after minimized to tray, so we need to check if window is mapped
|
||||
// if it is mapped and not set as skip_taskbar, we must add it to our task list
|
||||
XWindowAttributes wa;
|
||||
XGetWindowAttributes(server.display, win, &wa);
|
||||
if (wa.map_state == IsViewable && !window_is_skip_taskbar(win)) {
|
||||
if ((task = add_task(win)))
|
||||
schedule_panel_redraw();
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
// fprintf(stderr, "tint2: atom root_win = %s, %s\n", XGetAtomName(server.display, at), task->title);
|
||||
|
||||
// Window title changed
|
||||
if (at == server.atom._NET_WM_VISIBLE_NAME || at == server.atom._NET_WM_NAME || at == server.atom.WM_NAME) {
|
||||
if (task_update_title(task)) {
|
||||
if (g_tooltip.mapped && (g_tooltip.area == (Area *)task)) {
|
||||
tooltip_update_contents_for((Area *)task);
|
||||
tooltip_update();
|
||||
}
|
||||
if (taskbar_sort_method == TASKBAR_SORT_TITLE)
|
||||
sort_taskbar_for_win(win);
|
||||
schedule_panel_redraw();
|
||||
}
|
||||
}
|
||||
// Demand attention
|
||||
else if (at == server.atom._NET_WM_STATE) {
|
||||
if (debug) {
|
||||
int count;
|
||||
Atom *atom_state = server_get_property(win, server.atom._NET_WM_STATE, XA_ATOM, &count);
|
||||
for (int j = 0; j < count; j++) {
|
||||
char *atom_state_name = XGetAtomName(server.display, atom_state[j]);
|
||||
fprintf(stderr, "tint2: %s %d: _NET_WM_STATE = %s\n", __func__, __LINE__, atom_state_name);
|
||||
XFree(atom_state_name);
|
||||
}
|
||||
XFree(atom_state);
|
||||
}
|
||||
if (window_is_urgent(win)) {
|
||||
add_urgent(task);
|
||||
}
|
||||
if (window_is_skip_taskbar(win)) {
|
||||
remove_task(task);
|
||||
schedule_panel_redraw();
|
||||
}
|
||||
} else if (at == server.atom.WM_STATE) {
|
||||
// Iconic state
|
||||
TaskState state = (active_task && task->win == active_task->win ? TASK_ACTIVE : TASK_NORMAL);
|
||||
if (window_is_iconified(win))
|
||||
state = TASK_ICONIFIED;
|
||||
set_task_state(task, state);
|
||||
schedule_panel_redraw();
|
||||
}
|
||||
// Window icon changed
|
||||
else if (at == server.atom._NET_WM_ICON) {
|
||||
task_update_icon(task);
|
||||
schedule_panel_redraw();
|
||||
}
|
||||
// Window desktop changed
|
||||
else if (at == server.atom._NET_WM_DESKTOP) {
|
||||
int desktop = get_window_desktop(win);
|
||||
// fprintf(stderr, "tint2: Window desktop changed %d, %d\n", task->desktop, desktop);
|
||||
// bug in windowmaker : send unecessary 'desktop changed' when focus changed
|
||||
if (desktop != task->desktop) {
|
||||
task_update_desktop(task);
|
||||
}
|
||||
} else if (at == server.atom.WM_HINTS) {
|
||||
XWMHints *wmhints = XGetWMHints(server.display, win);
|
||||
if (wmhints && wmhints->flags & XUrgencyHint) {
|
||||
add_urgent(task);
|
||||
}
|
||||
XFree(wmhints);
|
||||
task_update_icon(task);
|
||||
schedule_panel_redraw();
|
||||
}
|
||||
|
||||
if (!server.got_root_win)
|
||||
server.root_win = RootWindow(server.display, server.screen);
|
||||
}
|
||||
}
|
||||
|
||||
void handle_event_expose(XEvent *e)
|
||||
{
|
||||
Panel *panel;
|
||||
panel = get_panel(e->xany.window);
|
||||
if (!panel)
|
||||
return;
|
||||
// TODO : one panel_refresh per panel ?
|
||||
schedule_panel_redraw();
|
||||
}
|
||||
|
||||
void handle_event_configure_notify(XEvent *e)
|
||||
{
|
||||
Window win = e->xconfigure.window;
|
||||
|
||||
// change in root window (xrandr)
|
||||
if (win == server.root_win) {
|
||||
emit_self_restart("configuration change in the root window");
|
||||
return;
|
||||
}
|
||||
|
||||
TrayWindow *traywin = systray_find_icon(win);
|
||||
if (traywin) {
|
||||
systray_reconfigure_event(traywin, e);
|
||||
return;
|
||||
}
|
||||
|
||||
// 'win' move in another monitor
|
||||
if (num_panels > 1 || hide_task_diff_monitor) {
|
||||
Task *task = get_task(win);
|
||||
if (task) {
|
||||
Panel *p = task->area.panel;
|
||||
int monitor = get_window_monitor(win);
|
||||
if ((hide_task_diff_monitor && p->monitor != monitor && task->area.on_screen) ||
|
||||
(hide_task_diff_monitor && p->monitor == monitor && !task->area.on_screen) ||
|
||||
(p->monitor != monitor && num_panels > 1)) {
|
||||
remove_task(task);
|
||||
task = add_task(win);
|
||||
if (win == get_active_window()) {
|
||||
set_task_state(task, TASK_ACTIVE);
|
||||
active_task = task;
|
||||
}
|
||||
schedule_panel_redraw();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (server.viewports) {
|
||||
Task *task = get_task(win);
|
||||
if (task) {
|
||||
int desktop = get_window_desktop(win);
|
||||
if (task->desktop != desktop) {
|
||||
task_update_desktop(task);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sort_taskbar_for_win(win);
|
||||
}
|
||||
|
||||
gboolean handle_x_event_autohide(XEvent *e)
|
||||
{
|
||||
Panel *panel = get_panel(e->xany.window);
|
||||
if (panel && panel_autohide) {
|
||||
if (e->type == EnterNotify)
|
||||
autohide_trigger_show(panel);
|
||||
else if (e->type == LeaveNotify)
|
||||
autohide_trigger_hide(panel);
|
||||
if (panel->is_hidden) {
|
||||
if (e->type == ClientMessage && e->xclient.message_type == server.atom.XdndPosition) {
|
||||
hidden_panel_shown_for_dnd = TRUE;
|
||||
autohide_show(panel);
|
||||
} else {
|
||||
// discard further processing of this event because the panel is not visible yet
|
||||
return TRUE;
|
||||
}
|
||||
} else if (hidden_panel_shown_for_dnd && e->type == ClientMessage &&
|
||||
e->xclient.message_type == server.atom.XdndLeave) {
|
||||
hidden_panel_shown_for_dnd = FALSE;
|
||||
autohide_hide(panel);
|
||||
}
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
void handle_x_event(XEvent *e)
|
||||
{
|
||||
#if HAVE_SN
|
||||
if (startup_notifications)
|
||||
sn_display_process_event(server.sn_display, e);
|
||||
#endif // HAVE_SN
|
||||
|
||||
if (handle_x_event_autohide(e))
|
||||
return;
|
||||
|
||||
Panel *panel = get_panel(e->xany.window);
|
||||
switch (e->type) {
|
||||
case ButtonPress: {
|
||||
tooltip_hide(0);
|
||||
handle_mouse_press_event(e);
|
||||
Area *area = find_area_under_mouse(panel, e->xbutton.x, e->xbutton.y);
|
||||
if (panel_config.mouse_effects)
|
||||
mouse_over(area, TRUE);
|
||||
break;
|
||||
}
|
||||
|
||||
case ButtonRelease: {
|
||||
handle_mouse_release_event(e);
|
||||
Area *area = find_area_under_mouse(panel, e->xbutton.x, e->xbutton.y);
|
||||
if (panel_config.mouse_effects)
|
||||
mouse_over(area, FALSE);
|
||||
break;
|
||||
}
|
||||
|
||||
case MotionNotify: {
|
||||
unsigned int button_mask = Button1Mask | Button2Mask | Button3Mask | Button4Mask | Button5Mask;
|
||||
if (e->xmotion.state & button_mask)
|
||||
handle_mouse_move_event(e);
|
||||
|
||||
Area *area = find_area_under_mouse(panel, e->xmotion.x, e->xmotion.y);
|
||||
if (area->_get_tooltip_text)
|
||||
tooltip_trigger_show(area, panel, e);
|
||||
else
|
||||
tooltip_trigger_hide();
|
||||
if (panel_config.mouse_effects)
|
||||
mouse_over(area, e->xmotion.state & button_mask);
|
||||
break;
|
||||
}
|
||||
|
||||
case LeaveNotify: {
|
||||
tooltip_trigger_hide();
|
||||
if (panel_config.mouse_effects)
|
||||
mouse_out();
|
||||
break;
|
||||
}
|
||||
|
||||
case Expose:
|
||||
handle_event_expose(e);
|
||||
break;
|
||||
|
||||
case PropertyNotify:
|
||||
handle_event_property_notify(e);
|
||||
break;
|
||||
|
||||
case ConfigureNotify:
|
||||
handle_event_configure_notify(e);
|
||||
break;
|
||||
|
||||
case ConfigureRequest: {
|
||||
TrayWindow *traywin = systray_find_icon(e->xany.window);
|
||||
if (traywin)
|
||||
systray_reconfigure_event(traywin, e);
|
||||
break;
|
||||
}
|
||||
|
||||
case ResizeRequest: {
|
||||
TrayWindow *traywin = systray_find_icon(e->xany.window);
|
||||
if (traywin)
|
||||
systray_resize_request_event(traywin, e);
|
||||
break;
|
||||
}
|
||||
|
||||
case ReparentNotify: {
|
||||
if (!systray_enabled)
|
||||
break;
|
||||
Panel *systray_panel = (Panel *)systray.area.panel;
|
||||
if (e->xany.window == systray_panel->main_win) // don't care
|
||||
break;
|
||||
TrayWindow *traywin = systray_find_icon(e->xreparent.window);
|
||||
if (traywin) {
|
||||
if (traywin->win == e->xreparent.window) {
|
||||
if (traywin->parent == e->xreparent.parent) {
|
||||
embed_icon(traywin);
|
||||
} else {
|
||||
remove_icon(traywin);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case UnmapNotify:
|
||||
break;
|
||||
|
||||
case DestroyNotify:
|
||||
if (e->xany.window == server.composite_manager) {
|
||||
// Stop real_transparency
|
||||
emit_self_restart("compositor shutdown");
|
||||
break;
|
||||
}
|
||||
if (e->xany.window == g_tooltip.window || !systray_enabled)
|
||||
break;
|
||||
for (GSList *it = systray.list_icons; it; it = g_slist_next(it)) {
|
||||
if (((TrayWindow *)it->data)->win == e->xany.window) {
|
||||
systray_destroy_event((TrayWindow *)it->data);
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case ClientMessage: {
|
||||
XClientMessageEvent *ev = &e->xclient;
|
||||
if (ev->data.l[1] == server.atom._NET_WM_CM_S0) {
|
||||
if (ev->data.l[2] == None) {
|
||||
// Stop real_transparency
|
||||
emit_self_restart("compositor changed");
|
||||
} else {
|
||||
// Start real_transparency
|
||||
emit_self_restart("compositor changed");
|
||||
}
|
||||
}
|
||||
if (systray_enabled && e->xclient.message_type == server.atom._NET_SYSTEM_TRAY_OPCODE &&
|
||||
e->xclient.format == 32 && e->xclient.window == net_sel_win) {
|
||||
handle_systray_event(&e->xclient);
|
||||
} else if (e->xclient.message_type == server.atom.XdndEnter) {
|
||||
handle_dnd_enter(&e->xclient);
|
||||
} else if (e->xclient.message_type == server.atom.XdndPosition) {
|
||||
handle_dnd_position(&e->xclient);
|
||||
} else if (e->xclient.message_type == server.atom.XdndDrop) {
|
||||
handle_dnd_drop(&e->xclient);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case SelectionNotify: {
|
||||
handle_dnd_selection_notify(&e->xselection);
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
if (e->type == server.xdamage_event_type) {
|
||||
XDamageNotifyEvent *de = (XDamageNotifyEvent *)e;
|
||||
TrayWindow *traywin = systray_find_icon(de->drawable);
|
||||
if (traywin)
|
||||
systray_render_icon(traywin);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void handle_x_events()
|
||||
{
|
||||
if (XPending(server.display) > 0) {
|
||||
XEvent e;
|
||||
XNextEvent(server.display, &e);
|
||||
if (debug_fps)
|
||||
ts_event_read = get_time();
|
||||
|
||||
handle_x_event(&e);
|
||||
}
|
||||
}
|
||||
|
||||
void prepare_fd_set(fd_set *set, int *max_fd)
|
||||
{
|
||||
FD_ZERO(set);
|
||||
FD_SET(server.x11_fd, set);
|
||||
*max_fd = server.x11_fd;
|
||||
if (sigchild_pipe_valid) {
|
||||
FD_SET(sigchild_pipe[0], set);
|
||||
*max_fd = MAX(*max_fd, sigchild_pipe[0]);
|
||||
}
|
||||
for (GList *l = panel_config.execp_list; l; l = l->next) {
|
||||
Execp *execp = (Execp *)l->data;
|
||||
int fd = execp->backend->child_pipe_stdout;
|
||||
if (fd > 0) {
|
||||
FD_SET(fd, set);
|
||||
*max_fd = MAX(*max_fd, fd);
|
||||
}
|
||||
fd = execp->backend->child_pipe_stderr;
|
||||
if (fd > 0) {
|
||||
FD_SET(fd, set);
|
||||
*max_fd = MAX(*max_fd, fd);
|
||||
}
|
||||
}
|
||||
if (uevent_fd > 0) {
|
||||
FD_SET(uevent_fd, set);
|
||||
*max_fd = MAX(*max_fd, uevent_fd);
|
||||
}
|
||||
}
|
||||
|
||||
void handle_panel_refresh()
|
||||
{
|
||||
if (debug_fps)
|
||||
ts_event_processed = get_time();
|
||||
panel_refresh = FALSE;
|
||||
|
||||
for (int i = 0; i < num_panels; i++) {
|
||||
Panel *panel = &panels[i];
|
||||
if (!first_render)
|
||||
shrink_panel(panel);
|
||||
|
||||
if (!panel->is_hidden || panel->area.resize_needed) {
|
||||
if (panel->temp_pmap)
|
||||
XFreePixmap(server.display, panel->temp_pmap);
|
||||
panel->temp_pmap = XCreatePixmap(server.display,
|
||||
server.root_win,
|
||||
panel->area.width,
|
||||
panel->area.height,
|
||||
server.depth);
|
||||
render_panel(panel);
|
||||
}
|
||||
|
||||
if (panel->is_hidden) {
|
||||
if (!panel->hidden_pixmap) {
|
||||
panel->hidden_pixmap = XCreatePixmap(server.display,
|
||||
server.root_win,
|
||||
panel->hidden_width,
|
||||
panel->hidden_height,
|
||||
server.depth);
|
||||
int xoff = 0, yoff = 0;
|
||||
if (panel_horizontal && panel_position & BOTTOM)
|
||||
yoff = panel->area.height - panel->hidden_height;
|
||||
else if (!panel_horizontal && panel_position & RIGHT)
|
||||
xoff = panel->area.width - panel->hidden_width;
|
||||
XCopyArea(server.display,
|
||||
panel->area.pix,
|
||||
panel->hidden_pixmap,
|
||||
server.gc,
|
||||
xoff,
|
||||
yoff,
|
||||
panel->hidden_width,
|
||||
panel->hidden_height,
|
||||
0,
|
||||
0);
|
||||
}
|
||||
XCopyArea(server.display,
|
||||
panel->hidden_pixmap,
|
||||
panel->main_win,
|
||||
server.gc,
|
||||
0,
|
||||
0,
|
||||
panel->hidden_width,
|
||||
panel->hidden_height,
|
||||
0,
|
||||
0);
|
||||
XSetWindowBackgroundPixmap(server.display, panel->main_win, panel->hidden_pixmap);
|
||||
} else {
|
||||
XCopyArea(server.display,
|
||||
panel->temp_pmap,
|
||||
panel->main_win,
|
||||
server.gc,
|
||||
0,
|
||||
0,
|
||||
panel->area.width,
|
||||
panel->area.height,
|
||||
0,
|
||||
0);
|
||||
if (panel == (Panel *)systray.area.panel) {
|
||||
if (refresh_systray && panel && !panel->is_hidden) {
|
||||
refresh_systray = FALSE;
|
||||
XSetWindowBackgroundPixmap(server.display, panel->main_win, panel->temp_pmap);
|
||||
refresh_systray_icons();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (first_render) {
|
||||
first_render = FALSE;
|
||||
if (panel_shrink)
|
||||
schedule_panel_redraw();
|
||||
}
|
||||
|
||||
if (debug_fps)
|
||||
ts_render_finished = get_time();
|
||||
XFlush(server.display);
|
||||
|
||||
if (debug_fps && ts_event_read > 0) {
|
||||
ts_flush_finished = get_time();
|
||||
double period = ts_flush_finished - ts_event_read;
|
||||
double fps = 1.0 / period;
|
||||
sample_fps(fps);
|
||||
double proc_ratio = (ts_event_processed - ts_event_read) / period;
|
||||
double render_ratio = (ts_render_finished - ts_event_processed) / period;
|
||||
double flush_ratio = (ts_flush_finished - ts_render_finished) / period;
|
||||
double fps_low, fps_median, fps_high, fps_samples;
|
||||
fps_compute_stats(&fps_low, &fps_median, &fps_high, &fps_samples);
|
||||
fprintf(stderr,
|
||||
BLUE "frame %d: fps = %.0f (low %.0f, med %.0f, high %.0f, samples %.0f) : processing %.0f%%, "
|
||||
"rendering %.0f%%, "
|
||||
"flushing %.0f%%" RESET "\n",
|
||||
frame,
|
||||
fps,
|
||||
fps_low,
|
||||
fps_median,
|
||||
fps_high,
|
||||
fps_samples,
|
||||
proc_ratio * 100,
|
||||
render_ratio * 100,
|
||||
flush_ratio * 100);
|
||||
#ifdef HAVE_TRACING
|
||||
stop_tracing();
|
||||
if (fps <= tracing_fps_threshold) {
|
||||
print_tracing_events();
|
||||
}
|
||||
#endif
|
||||
}
|
||||
if (debug_frames) {
|
||||
for (int i = 0; i < num_panels; i++) {
|
||||
char path[256];
|
||||
snprintf(path, sizeof(path), "tint2-%d-panel-%d-frame-%d.png", getpid(), i, frame);
|
||||
save_panel_screenshot(&panels[i], path);
|
||||
}
|
||||
}
|
||||
frame++;
|
||||
}
|
||||
|
||||
void run_tint2_event_loop()
|
||||
{
|
||||
ts_event_read = 0;
|
||||
ts_event_processed = 0;
|
||||
ts_render_finished = 0;
|
||||
ts_flush_finished = 0;
|
||||
first_render = TRUE;
|
||||
|
||||
while (!get_signal_pending()) {
|
||||
if (panel_refresh)
|
||||
handle_panel_refresh();
|
||||
|
||||
fd_set fds;
|
||||
int max_fd;
|
||||
prepare_fd_set(&fds, &max_fd);
|
||||
|
||||
// Wait for an event and handle it
|
||||
ts_event_read = 0;
|
||||
if (XPending(server.display) > 0 || select(max_fd + 1, &fds, 0, 0, get_duration_to_next_timer_expiration()) >= 0) {
|
||||
#ifdef HAVE_TRACING
|
||||
start_tracing((void*)run_tint2_event_loop);
|
||||
#endif
|
||||
uevent_handler();
|
||||
handle_sigchld_events();
|
||||
handle_execp_events();
|
||||
handle_x_events();
|
||||
}
|
||||
|
||||
handle_expired_timers();
|
||||
}
|
||||
}
|
||||
|
||||
void tint2(int argc, char **argv, gboolean *restart)
|
||||
{
|
||||
init(argc, argv);
|
||||
|
||||
if (snapshot_path) {
|
||||
save_screenshot(snapshot_path);
|
||||
cleanup();
|
||||
return;
|
||||
}
|
||||
|
||||
dnd_init();
|
||||
uevent_init();
|
||||
run_tint2_event_loop();
|
||||
|
||||
if (get_signal_pending()) {
|
||||
cleanup();
|
||||
if (get_signal_pending() == SIGUSR1) {
|
||||
fprintf(stderr, YELLOW "tint2: %s %d: restarting tint2..." RESET "\n", __FILE__, __LINE__);
|
||||
*restart = TRUE;
|
||||
return;
|
||||
} else if (get_signal_pending() == SIGUSR2) {
|
||||
fprintf(stderr, YELLOW "tint2: %s %d: reexecuting tint2..." RESET "\n", __FILE__, __LINE__);
|
||||
if (execvp(argv[0], argv) == -1) {
|
||||
fprintf(stderr, RED "tint2: %s %d: failed!" RESET "\n", __FILE__, __LINE__);
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
// SIGINT, SIGTERM, SIGHUP etc.
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
gboolean restart;
|
||||
do {
|
||||
restart = FALSE;
|
||||
tint2(argc, argv, &restart);
|
||||
} while(restart);
|
||||
return 0;
|
||||
}
|
||||
287
src/mouse_actions.c
Normal file
287
src/mouse_actions.c
Normal file
@@ -0,0 +1,287 @@
|
||||
/**************************************************************************
|
||||
* Copyright (C) 2017 tint2 authors
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License version 2
|
||||
* as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
**************************************************************************/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "drag_and_drop.h"
|
||||
#include "panel.h"
|
||||
#include "server.h"
|
||||
#include "task.h"
|
||||
#include "window.h"
|
||||
|
||||
gboolean tint2_handles_click(Panel *panel, XButtonEvent *e)
|
||||
{
|
||||
Task *task = click_task(panel, e->x, e->y);
|
||||
if (task) {
|
||||
if ((e->button == 1 && mouse_left != 0) || (e->button == 2 && mouse_middle != 0) ||
|
||||
(e->button == 3 && mouse_right != 0) || (e->button == 4 && mouse_scroll_up != 0) ||
|
||||
(e->button == 5 && mouse_scroll_down != 0)) {
|
||||
return TRUE;
|
||||
} else
|
||||
return FALSE;
|
||||
}
|
||||
LauncherIcon *icon = click_launcher_icon(panel, e->x, e->y);
|
||||
if (icon) {
|
||||
if (e->button == 1) {
|
||||
return TRUE;
|
||||
} else {
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
// no launcher/task clicked --> check if taskbar clicked
|
||||
Taskbar *taskbar = click_taskbar(panel, e->x, e->y);
|
||||
if (taskbar && e->button == 1 && taskbar_mode == MULTI_DESKTOP)
|
||||
return TRUE;
|
||||
if (click_clock(panel, e->x, e->y)) {
|
||||
if ((e->button == 1 && clock_lclick_command) || (e->button == 2 && clock_mclick_command) ||
|
||||
(e->button == 3 && clock_rclick_command) || (e->button == 4 && clock_uwheel_command) ||
|
||||
(e->button == 5 && clock_dwheel_command))
|
||||
return TRUE;
|
||||
else
|
||||
return FALSE;
|
||||
}
|
||||
#ifdef ENABLE_BATTERY
|
||||
if (click_battery(panel, e->x, e->y)) {
|
||||
if ((e->button == 1 && battery_lclick_command) || (e->button == 2 && battery_mclick_command) ||
|
||||
(e->button == 3 && battery_rclick_command) || (e->button == 4 && battery_uwheel_command) ||
|
||||
(e->button == 5 && battery_dwheel_command))
|
||||
return TRUE;
|
||||
else
|
||||
return FALSE;
|
||||
}
|
||||
#endif
|
||||
if (click_execp(panel, e->x, e->y))
|
||||
return TRUE;
|
||||
if (click_button(panel, e->x, e->y))
|
||||
return TRUE;
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
void handle_mouse_press_event(XEvent *e)
|
||||
{
|
||||
Panel *panel = get_panel(e->xany.window);
|
||||
if (!panel)
|
||||
return;
|
||||
|
||||
if (wm_menu && !tint2_handles_click(panel, &e->xbutton)) {
|
||||
forward_click(e);
|
||||
return;
|
||||
}
|
||||
task_drag = click_task(panel, e->xbutton.x, e->xbutton.y);
|
||||
|
||||
if (panel_layer == BOTTOM_LAYER)
|
||||
XLowerWindow(server.display, panel->main_win);
|
||||
}
|
||||
|
||||
void handle_mouse_move_event(XEvent *e)
|
||||
{
|
||||
Panel *panel = get_panel(e->xany.window);
|
||||
if (!panel || !task_drag)
|
||||
return;
|
||||
|
||||
// Find the taskbar on the event's location
|
||||
Taskbar *event_taskbar = click_taskbar(panel, e->xbutton.x, e->xbutton.y);
|
||||
if (event_taskbar == NULL)
|
||||
return;
|
||||
|
||||
// Find the task on the event's location
|
||||
Task *event_task = click_task(panel, e->xbutton.x, e->xbutton.y);
|
||||
|
||||
// If the event takes place on the same taskbar as the task being dragged
|
||||
if (&event_taskbar->area == task_drag->area.parent) {
|
||||
if (taskbar_sort_method != TASKBAR_NOSORT) {
|
||||
sort_tasks(event_taskbar);
|
||||
} else {
|
||||
// Swap the task_drag with the task on the event's location (if they differ)
|
||||
if (event_task && event_task != task_drag) {
|
||||
GList *drag_iter = g_list_find(event_taskbar->area.children, task_drag);
|
||||
GList *task_iter = g_list_find(event_taskbar->area.children, event_task);
|
||||
if (drag_iter && task_iter) {
|
||||
gpointer temp = task_iter->data;
|
||||
task_iter->data = drag_iter->data;
|
||||
drag_iter->data = temp;
|
||||
event_taskbar->area.resize_needed = 1;
|
||||
schedule_panel_redraw();
|
||||
task_dragged = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else { // The event is on another taskbar than the task being dragged
|
||||
if (task_drag->desktop == ALL_DESKTOPS || taskbar_mode != MULTI_DESKTOP)
|
||||
return;
|
||||
|
||||
Taskbar *drag_taskbar = (Taskbar *)task_drag->area.parent;
|
||||
remove_area((Area *)task_drag);
|
||||
|
||||
if (event_taskbar->area.posx > drag_taskbar->area.posx || event_taskbar->area.posy > drag_taskbar->area.posy) {
|
||||
int i = (taskbarname_enabled) ? 1 : 0;
|
||||
event_taskbar->area.children = g_list_insert(event_taskbar->area.children, task_drag, i);
|
||||
} else
|
||||
event_taskbar->area.children = g_list_append(event_taskbar->area.children, task_drag);
|
||||
|
||||
// Move task to other desktop (but avoid the 'Window desktop changed' code in 'event_property_notify')
|
||||
task_drag->area.parent = &event_taskbar->area;
|
||||
task_drag->desktop = event_taskbar->desktop;
|
||||
|
||||
change_window_desktop(task_drag->win, event_taskbar->desktop);
|
||||
if (hide_task_diff_desktop)
|
||||
change_desktop(event_taskbar->desktop);
|
||||
|
||||
if (taskbar_sort_method != TASKBAR_NOSORT) {
|
||||
sort_tasks(event_taskbar);
|
||||
}
|
||||
|
||||
event_taskbar->area.resize_needed = 1;
|
||||
drag_taskbar->area.resize_needed = 1;
|
||||
task_dragged = 1;
|
||||
schedule_panel_redraw();
|
||||
panel->area.resize_needed = 1;
|
||||
}
|
||||
}
|
||||
|
||||
void handle_mouse_release_event(XEvent *e)
|
||||
{
|
||||
Panel *panel = get_panel(e->xany.window);
|
||||
if (!panel)
|
||||
return;
|
||||
|
||||
if (wm_menu && !tint2_handles_click(panel, &e->xbutton)) {
|
||||
forward_click(e);
|
||||
if (panel_layer == BOTTOM_LAYER)
|
||||
XLowerWindow(server.display, panel->main_win);
|
||||
task_drag = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
MouseAction action = TOGGLE_ICONIFY;
|
||||
switch (e->xbutton.button) {
|
||||
case 1:
|
||||
action = mouse_left;
|
||||
break;
|
||||
case 2:
|
||||
action = mouse_middle;
|
||||
break;
|
||||
case 3:
|
||||
action = mouse_right;
|
||||
break;
|
||||
case 4:
|
||||
action = mouse_scroll_up;
|
||||
break;
|
||||
case 5:
|
||||
action = mouse_scroll_down;
|
||||
break;
|
||||
case 6:
|
||||
action = mouse_tilt_left;
|
||||
break;
|
||||
case 7:
|
||||
action = mouse_tilt_right;
|
||||
break;
|
||||
}
|
||||
|
||||
Clock *clock = click_clock(panel, e->xbutton.x, e->xbutton.y);
|
||||
if (clock) {
|
||||
clock_action(clock, e->xbutton.button, e->xbutton.x - clock->area.posx, e->xbutton.y - clock->area.posy, e->xbutton.time);
|
||||
if (panel_layer == BOTTOM_LAYER)
|
||||
XLowerWindow(server.display, panel->main_win);
|
||||
task_drag = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
#ifdef ENABLE_BATTERY
|
||||
Battery *battery = click_battery(panel, e->xbutton.x, e->xbutton.y);
|
||||
if (battery) {
|
||||
battery_action(battery, e->xbutton.button, e->xbutton.x - battery->area.posx, e->xbutton.y - battery->area.posy, e->xbutton.time);
|
||||
if (panel_layer == BOTTOM_LAYER)
|
||||
XLowerWindow(server.display, panel->main_win);
|
||||
task_drag = 0;
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
Execp *execp = click_execp(panel, e->xbutton.x, e->xbutton.y);
|
||||
if (execp) {
|
||||
execp_action(execp, e->xbutton.button, e->xbutton.x - execp->area.posx, e->xbutton.y - execp->area.posy, e->xbutton.time);
|
||||
if (panel_layer == BOTTOM_LAYER)
|
||||
XLowerWindow(server.display, panel->main_win);
|
||||
task_drag = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
Button *button = click_button(panel, e->xbutton.x, e->xbutton.y);
|
||||
if (button) {
|
||||
button_action(button, e->xbutton.button, e->xbutton.x - button->area.posx, e->xbutton.y - button->area.posy, e->xbutton.time);
|
||||
if (panel_layer == BOTTOM_LAYER)
|
||||
XLowerWindow(server.display, panel->main_win);
|
||||
task_drag = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
if (e->xbutton.button == 1 && click_launcher(panel, e->xbutton.x, e->xbutton.y)) {
|
||||
LauncherIcon *icon = click_launcher_icon(panel, e->xbutton.x, e->xbutton.y);
|
||||
if (icon) {
|
||||
launcher_action(icon, e, e->xbutton.x - icon->area.posx, e->xbutton.y - icon->area.posy);
|
||||
}
|
||||
task_drag = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
Taskbar *taskbar;
|
||||
if (!(taskbar = click_taskbar(panel, e->xbutton.x, e->xbutton.y))) {
|
||||
// TODO: check better solution to keep window below
|
||||
if (panel_layer == BOTTOM_LAYER)
|
||||
XLowerWindow(server.display, panel->main_win);
|
||||
task_drag = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
// drag and drop task
|
||||
if (task_dragged) {
|
||||
task_drag = 0;
|
||||
task_dragged = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
// switch desktop
|
||||
if (taskbar_mode == MULTI_DESKTOP) {
|
||||
gboolean diff_desktop = FALSE;
|
||||
if (taskbar->desktop != server.desktop && action != CLOSE && action != DESKTOP_LEFT &&
|
||||
action != DESKTOP_RIGHT) {
|
||||
diff_desktop = TRUE;
|
||||
change_desktop(taskbar->desktop);
|
||||
}
|
||||
Task *task = click_task(panel, e->xbutton.x, e->xbutton.y);
|
||||
if (task) {
|
||||
if (diff_desktop) {
|
||||
if (action == TOGGLE_ICONIFY) {
|
||||
if (!window_is_active(task->win))
|
||||
activate_window(task->win);
|
||||
} else {
|
||||
task_handle_mouse_event(task, action);
|
||||
}
|
||||
} else {
|
||||
task_handle_mouse_event(task, action);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
task_handle_mouse_event(click_task(panel, e->xbutton.x, e->xbutton.y), action);
|
||||
}
|
||||
|
||||
// to keep window below
|
||||
if (panel_layer == BOTTOM_LAYER)
|
||||
XLowerWindow(server.display, panel->main_win);
|
||||
}
|
||||
12
src/mouse_actions.h
Normal file
12
src/mouse_actions.h
Normal file
@@ -0,0 +1,12 @@
|
||||
#ifndef MOUSE_ACTIONS_H
|
||||
#define MOUSE_ACTIONS_H
|
||||
|
||||
#include "panel.h"
|
||||
|
||||
gboolean tint2_handles_click(Panel *panel, XButtonEvent *e);
|
||||
|
||||
void handle_mouse_press_event(XEvent *e);
|
||||
void handle_mouse_move_event(XEvent *e);
|
||||
void handle_mouse_release_event(XEvent *e);
|
||||
|
||||
#endif
|
||||
157
src/panel.c
157
src/panel.c
@@ -20,6 +20,8 @@
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
#include <X11/Xlib.h>
|
||||
#include <X11/Xutil.h>
|
||||
#include <X11/Xatom.h>
|
||||
@@ -36,8 +38,6 @@
|
||||
|
||||
void panel_clear_background(void *obj);
|
||||
|
||||
int signal_pending;
|
||||
|
||||
MouseAction mouse_left;
|
||||
MouseAction mouse_middle;
|
||||
MouseAction mouse_right;
|
||||
@@ -58,6 +58,7 @@ char *panel_window_name = NULL;
|
||||
gboolean debug_geometry;
|
||||
gboolean debug_gradients;
|
||||
gboolean startup_notifications;
|
||||
gboolean debug_thumbnails;
|
||||
|
||||
gboolean panel_autohide;
|
||||
int panel_autohide_show_timeout;
|
||||
@@ -129,8 +130,6 @@ void cleanup_panel()
|
||||
if (!panels)
|
||||
return;
|
||||
|
||||
cleanup_taskbar();
|
||||
|
||||
for (int i = 0; i < num_panels; i++) {
|
||||
Panel *p = &panels[i];
|
||||
|
||||
@@ -144,10 +143,12 @@ void cleanup_panel()
|
||||
if (p->main_win)
|
||||
XDestroyWindow(server.display, p->main_win);
|
||||
p->main_win = 0;
|
||||
stop_timeout(p->autohide_timeout);
|
||||
destroy_timer(&p->autohide_timer);
|
||||
cleanup_freespace(p);
|
||||
}
|
||||
|
||||
free_icon_themes();
|
||||
|
||||
free(panel_items_order);
|
||||
panel_items_order = NULL;
|
||||
free(panel_window_name);
|
||||
@@ -175,11 +176,11 @@ void init_panel()
|
||||
{
|
||||
if (panel_config.monitor > (server.num_monitors - 1)) {
|
||||
// server.num_monitors minimum value is 1 (see get_monitors())
|
||||
fprintf(stderr, "warning : monitor not found. tint2 default to all monitors.\n");
|
||||
fprintf(stderr, "tint2: warning : monitor not found. tint2 default to all monitors.\n");
|
||||
panel_config.monitor = 0;
|
||||
}
|
||||
|
||||
fprintf(stderr, "panel items: %s\n", panel_items_order);
|
||||
fprintf(stderr, "tint2: panel items: %s\n", panel_items_order);
|
||||
|
||||
icon_theme_wrapper = NULL;
|
||||
|
||||
@@ -204,10 +205,11 @@ void init_panel()
|
||||
panels = calloc(num_panels, sizeof(Panel));
|
||||
for (int i = 0; i < num_panels; i++) {
|
||||
memcpy(&panels[i], &panel_config, sizeof(Panel));
|
||||
INIT_TIMER(panels[i].autohide_timer);
|
||||
}
|
||||
|
||||
fprintf(stderr,
|
||||
"tint2 : nb monitor %d, nb monitor used %d, nb desktop %d\n",
|
||||
"tint2: nb monitors %d, nb monitors used %d, nb desktops %d\n",
|
||||
server.num_monitors,
|
||||
num_panels,
|
||||
server.num_desktops);
|
||||
@@ -287,7 +289,7 @@ void init_panel()
|
||||
XGCValues gcv;
|
||||
server.gc = XCreateGC(server.display, p->main_win, 0, &gcv);
|
||||
}
|
||||
// printf("panel %d : %d, %d, %d, %d\n", i, p->posx, p->posy, p->area.width, p->area.height);
|
||||
// fprintf(stderr, "tint2: panel %d : %d, %d, %d, %d\n", i, p->posx, p->posy, p->area.width, p->area.height);
|
||||
set_panel_properties(p);
|
||||
set_panel_background(p);
|
||||
if (!snapshot_path) {
|
||||
@@ -320,7 +322,7 @@ void panel_compute_size(Panel *panel)
|
||||
if (panel->fractional_height)
|
||||
panel->area.height = (server.monitors[panel->monitor].height - panel->marginy) * panel->area.height / 100;
|
||||
if (panel->area.bg->border.radius > panel->area.height / 2) {
|
||||
printf("panel_background_id rounded is too big... please fix your tint2rc\n");
|
||||
fprintf(stderr, "tint2: panel_background_id rounded is too big... please fix your tint2rc\n");
|
||||
g_array_append_val(backgrounds, *panel->area.bg);
|
||||
panel->area.bg = &g_array_index(backgrounds, Background, backgrounds->len - 1);
|
||||
panel->area.bg->border.radius = panel->area.height / 2;
|
||||
@@ -346,7 +348,7 @@ void panel_compute_size(Panel *panel)
|
||||
panel->area.width = old_panel_height;
|
||||
|
||||
if (panel->area.bg->border.radius > panel->area.width / 2) {
|
||||
printf("panel_background_id rounded is too big... please fix your tint2rc\n");
|
||||
fprintf(stderr, "tint2: panel_background_id rounded is too big... please fix your tint2rc\n");
|
||||
g_array_append_val(backgrounds, *panel->area.bg);
|
||||
panel->area.bg = &g_array_index(backgrounds, Background, backgrounds->len - 1);
|
||||
panel->area.bg->border.radius = panel->area.width / 2;
|
||||
@@ -399,7 +401,7 @@ void panel_compute_position(Panel *panel)
|
||||
panel->hidden_width = panel->area.width - diff;
|
||||
panel->hidden_height = panel->area.height;
|
||||
}
|
||||
// printf("panel : posx %d, posy %d, width %d, height %d\n", panel->posx, panel->posy, panel->area.width,
|
||||
// fprintf(stderr, "tint2: panel : posx %d, posy %d, width %d, height %d\n", panel->posx, panel->posy, panel->area.width,
|
||||
// panel->area.height);
|
||||
}
|
||||
|
||||
@@ -414,7 +416,7 @@ gboolean resize_panel(void *obj)
|
||||
Panel *panel = (Panel *)obj;
|
||||
relayout_with_constraint(&panel->area, 0);
|
||||
|
||||
// printf("resize_panel\n");
|
||||
// fprintf(stderr, "tint2: resize_panel\n");
|
||||
if (taskbar_mode != MULTI_DESKTOP && taskbar_enabled) {
|
||||
// propagate width/height on hidden taskbar
|
||||
int width = panel->taskbar[server.desktop].area.width;
|
||||
@@ -491,9 +493,11 @@ gboolean resize_panel(void *obj)
|
||||
}
|
||||
}
|
||||
|
||||
// Distribute the remaining size between tasks
|
||||
// Distribute the remaining size between taskbars
|
||||
if (num_tasks > 0) {
|
||||
int task_size = total_size / num_tasks;
|
||||
if (taskbar_alignment != ALIGN_LEFT)
|
||||
task_size = MIN(task_size, panel_horizontal ? panel_config.g_task.maximum_width : panel_config.g_task.maximum_height);
|
||||
for (int i = 0; i < panel->num_desktops; i++) {
|
||||
Taskbar *taskbar = &panel->taskbar[i];
|
||||
if (!taskbar->area.on_screen)
|
||||
@@ -510,6 +514,43 @@ 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;
|
||||
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;
|
||||
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;
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// No tasks => expand the first visible taskbar
|
||||
for (int i = 0; i < panel->num_desktops; i++) {
|
||||
@@ -551,7 +592,7 @@ void update_strut(Panel *p)
|
||||
long struts[12] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
|
||||
if (panel_horizontal) {
|
||||
int height = p->area.height + p->marginy;
|
||||
if (panel_strut_policy == STRUT_MINIMUM || (panel_strut_policy == STRUT_FOLLOW_SIZE && panel_autohide))
|
||||
if (panel_strut_policy == STRUT_MINIMUM || (panel_strut_policy == STRUT_FOLLOW_SIZE && panel_autohide && p->is_hidden))
|
||||
height = p->hidden_height;
|
||||
if (panel_position & TOP) {
|
||||
struts[2] = height + monitor.y;
|
||||
@@ -566,7 +607,7 @@ void update_strut(Panel *p)
|
||||
}
|
||||
} else {
|
||||
int width = p->area.width + p->marginx;
|
||||
if (panel_strut_policy == STRUT_MINIMUM || (panel_strut_policy == STRUT_FOLLOW_SIZE && panel_autohide))
|
||||
if (panel_strut_policy == STRUT_MINIMUM || (panel_strut_policy == STRUT_FOLLOW_SIZE && panel_autohide && p->is_hidden))
|
||||
width = p->hidden_width;
|
||||
if (panel_position & LEFT) {
|
||||
struts[0] = width + monitor.x;
|
||||
@@ -814,15 +855,24 @@ void set_panel_properties(Panel *p)
|
||||
g_free(name);
|
||||
}
|
||||
|
||||
long pid = getpid();
|
||||
XChangeProperty(server.display,
|
||||
p->main_win,
|
||||
server.atom._NET_WM_PID,
|
||||
XA_CARDINAL,
|
||||
32,
|
||||
PropModeReplace,
|
||||
(unsigned char *)&pid,
|
||||
1);
|
||||
|
||||
// Dock
|
||||
long val = server.atom._NET_WM_WINDOW_TYPE_DOCK;
|
||||
XChangeProperty(server.display,
|
||||
p->main_win,
|
||||
server.atom._NET_WM_WINDOW_TYPE,
|
||||
XA_ATOM,
|
||||
32,
|
||||
PropModeReplace,
|
||||
(unsigned char *)&val,
|
||||
(unsigned char *)&server.atom._NET_WM_WINDOW_TYPE_DOCK,
|
||||
1);
|
||||
|
||||
place_panel_all_desktops(p);
|
||||
@@ -1009,15 +1059,15 @@ Button *click_button(Panel *panel, int x, int y)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void stop_autohide_timeout(Panel *p)
|
||||
void stop_autohide_timer(Panel *p)
|
||||
{
|
||||
stop_timeout(p->autohide_timeout);
|
||||
stop_timer(&p->autohide_timer);
|
||||
}
|
||||
|
||||
void autohide_show(void *p)
|
||||
{
|
||||
Panel *panel = (Panel *)p;
|
||||
stop_autohide_timeout(panel);
|
||||
stop_autohide_timer(panel);
|
||||
panel->is_hidden = 0;
|
||||
XMapSubwindows(server.display, panel->main_win); // systray windows
|
||||
set_panel_window_geometry(panel);
|
||||
@@ -1029,7 +1079,7 @@ void autohide_show(void *p)
|
||||
void autohide_hide(void *p)
|
||||
{
|
||||
Panel *panel = (Panel *)p;
|
||||
stop_autohide_timeout(panel);
|
||||
stop_autohide_timer(panel);
|
||||
set_panel_layer(panel, panel_layer);
|
||||
panel->is_hidden = TRUE;
|
||||
XUnmapSubwindows(server.display, panel->main_win); // systray windows
|
||||
@@ -1041,7 +1091,7 @@ void autohide_trigger_show(Panel *p)
|
||||
{
|
||||
if (!p)
|
||||
return;
|
||||
change_timeout(&p->autohide_timeout, panel_autohide_show_timeout, 0, autohide_show, p);
|
||||
change_timer(&p->autohide_timer, true, panel_autohide_show_timeout, 0, autohide_show, p);
|
||||
}
|
||||
|
||||
void autohide_trigger_hide(Panel *p)
|
||||
@@ -1056,7 +1106,7 @@ void autohide_trigger_hide(Panel *p)
|
||||
if (child)
|
||||
return; // mouse over one of the system tray icons
|
||||
|
||||
change_timeout(&p->autohide_timeout, panel_autohide_hide_timeout, 0, autohide_hide, p);
|
||||
change_timer(&p->autohide_timer, true, panel_autohide_hide_timeout, 0, autohide_hide, p);
|
||||
}
|
||||
|
||||
void shrink_panel(Panel *panel)
|
||||
@@ -1135,6 +1185,63 @@ void _schedule_panel_redraw(const char *file, const char *function, const int li
|
||||
{
|
||||
panel_refresh = TRUE;
|
||||
if (debug_fps) {
|
||||
fprintf(stderr, YELLOW "%s %s %d: triggering panel redraw" RESET "\n", file, function, line);
|
||||
fprintf(stderr, YELLOW "tint2: %s %s %d: triggering panel redraw" RESET "\n", file, function, line);
|
||||
}
|
||||
}
|
||||
|
||||
void save_panel_screenshot(const Panel *panel, const char *path)
|
||||
{
|
||||
imlib_context_set_drawable(panel->temp_pmap);
|
||||
Imlib_Image img = imlib_create_image_from_drawable(0, 0, 0, panel->area.width, panel->area.height, 1);
|
||||
|
||||
if (!img) {
|
||||
XImage *ximg =
|
||||
XGetImage(server.display, panel->temp_pmap, 0, 0, panel->area.width, panel->area.height, AllPlanes, ZPixmap);
|
||||
|
||||
if (ximg) {
|
||||
DATA32 *pixels = (DATA32 *)calloc(panel->area.width * panel->area.height, sizeof(DATA32));
|
||||
for (int x = 0; x < panel->area.width; x++) {
|
||||
for (int y = 0; y < panel->area.height; y++) {
|
||||
DATA32 xpixel = XGetPixel(ximg, x, y);
|
||||
|
||||
DATA32 r = (xpixel >> 16) & 0xff;
|
||||
DATA32 g = (xpixel >> 8) & 0xff;
|
||||
DATA32 b = (xpixel >> 0) & 0xff;
|
||||
DATA32 a = 0x0;
|
||||
|
||||
DATA32 argb = (a << 24) | (r << 16) | (g << 8) | b;
|
||||
pixels[y * panel->area.width + x] = argb;
|
||||
}
|
||||
}
|
||||
XDestroyImage(ximg);
|
||||
img = imlib_create_image_using_data(panel->area.width, panel->area.height, pixels);
|
||||
}
|
||||
}
|
||||
|
||||
if (img) {
|
||||
imlib_context_set_image(img);
|
||||
if (!panel_horizontal) {
|
||||
// rotate 90° vertical panel
|
||||
imlib_image_flip_horizontal();
|
||||
imlib_image_flip_diagonal();
|
||||
}
|
||||
imlib_save_image(path);
|
||||
imlib_free_image();
|
||||
}
|
||||
}
|
||||
|
||||
void save_screenshot(const char *path)
|
||||
{
|
||||
Panel *panel = &panels[0];
|
||||
|
||||
if (panel->area.width > server.monitors[0].width)
|
||||
panel->area.width = server.monitors[0].width;
|
||||
|
||||
panel->temp_pmap =
|
||||
XCreatePixmap(server.display, server.root_win, panel->area.width, panel->area.height, server.depth);
|
||||
render_panel(panel);
|
||||
|
||||
XSync(server.display, False);
|
||||
|
||||
save_panel_screenshot(panel, path);
|
||||
}
|
||||
|
||||
10
src/panel.h
10
src/panel.h
@@ -29,7 +29,6 @@
|
||||
#include "battery.h"
|
||||
#endif
|
||||
|
||||
extern int signal_pending;
|
||||
// --------------------------------------------------
|
||||
// mouse events
|
||||
extern MouseAction mouse_left;
|
||||
@@ -93,7 +92,9 @@ extern XSettingsClient *xsettings_client;
|
||||
extern gboolean startup_notifications;
|
||||
extern gboolean debug_geometry;
|
||||
extern gboolean debug_fps;
|
||||
extern double tracing_fps_threshold;
|
||||
extern gboolean debug_frames;
|
||||
extern gboolean debug_thumbnails;
|
||||
|
||||
typedef struct Panel {
|
||||
Area area;
|
||||
@@ -143,7 +144,7 @@ typedef struct Panel {
|
||||
gboolean is_hidden;
|
||||
int hidden_width, hidden_height;
|
||||
Pixmap hidden_pixmap;
|
||||
timeout *autohide_timeout;
|
||||
Timer autohide_timer;
|
||||
} Panel;
|
||||
|
||||
extern Panel panel_config;
|
||||
@@ -165,7 +166,7 @@ gboolean resize_panel(void *obj);
|
||||
void render_panel(Panel *panel);
|
||||
void shrink_panel(Panel *panel);
|
||||
void _schedule_panel_redraw(const char *file, const char *function, const int line);
|
||||
#define schedule_panel_redraw() _schedule_panel_redraw(__FILE__, __FUNCTION__, __LINE__)
|
||||
#define schedule_panel_redraw() _schedule_panel_redraw(__FILE__, __func__, __LINE__)
|
||||
|
||||
void set_panel_items_order(Panel *p);
|
||||
void place_panel_all_desktops(Panel *p);
|
||||
@@ -207,4 +208,7 @@ void default_font_changed();
|
||||
void free_icon(Imlib_Image icon);
|
||||
Imlib_Image scale_icon(Imlib_Image original, int icon_size);
|
||||
|
||||
void save_screenshot(const char *path);
|
||||
void save_panel_screenshot(const Panel *panel, const char *path);
|
||||
|
||||
#endif
|
||||
|
||||
@@ -114,10 +114,10 @@ void init_systray()
|
||||
return;
|
||||
|
||||
systray_composited = !server.disable_transparency && server.visual32 && server.colormap32;
|
||||
fprintf(stderr, "Systray composited rendering %s\n", systray_composited ? "on" : "off");
|
||||
fprintf(stderr, "tint2: Systray composited rendering %s\n", systray_composited ? "on" : "off");
|
||||
|
||||
if (!systray_composited) {
|
||||
fprintf(stderr, "systray_asb forced to 100 0 0\n");
|
||||
fprintf(stderr, "tint2: systray_asb forced to 100 0 0\n");
|
||||
systray.alpha = 100;
|
||||
systray.brightness = systray.saturation = 0;
|
||||
}
|
||||
@@ -184,7 +184,7 @@ int systray_compute_desired_size(void *obj)
|
||||
gboolean resize_systray(void *obj)
|
||||
{
|
||||
if (systray_profile)
|
||||
fprintf(stderr, "[%f] %s:%d\n", profiling_get_time(), __FUNCTION__, __LINE__);
|
||||
fprintf(stderr, "tint2: [%f] %s:%d\n", profiling_get_time(), __func__, __LINE__);
|
||||
|
||||
int size;
|
||||
systray_compute_geometry(&size);
|
||||
@@ -227,7 +227,7 @@ gboolean resize_systray(void *obj)
|
||||
void draw_systray(void *obj, cairo_t *c)
|
||||
{
|
||||
if (systray_profile)
|
||||
fprintf(stderr, BLUE "[%f] %s:%d" RESET "\n", profiling_get_time(), __FUNCTION__, __LINE__);
|
||||
fprintf(stderr, BLUE "tint2: [%f] %s:%d" RESET "\n", profiling_get_time(), __func__, __LINE__);
|
||||
if (systray_composited) {
|
||||
if (render_background)
|
||||
XFreePixmap(server.display, render_background);
|
||||
@@ -252,12 +252,12 @@ void systray_dump_geometry(void *obj, int indent)
|
||||
{
|
||||
Systray *tray = (Systray *)obj;
|
||||
|
||||
fprintf(stderr, "%*sIcons:\n", indent, "");
|
||||
fprintf(stderr, "tint2: %*sIcons:\n", indent, "");
|
||||
indent += 2;
|
||||
for (GSList *l = tray->list_icons; l; l = l->next) {
|
||||
TrayWindow *traywin = (TrayWindow *)l->data;
|
||||
fprintf(stderr,
|
||||
"%*sIcon: x = %d, y = %d, w = %d, h = %d, name = %s\n",
|
||||
"tint2: %*sIcon: x = %d, y = %d, w = %d, h = %d, name = %s\n",
|
||||
indent,
|
||||
"",
|
||||
traywin->x,
|
||||
@@ -271,7 +271,7 @@ void systray_dump_geometry(void *obj, int indent)
|
||||
void on_change_systray(void *obj)
|
||||
{
|
||||
if (systray_profile)
|
||||
fprintf(stderr, "[%f] %s:%d\n", profiling_get_time(), __FUNCTION__, __LINE__);
|
||||
fprintf(stderr, "tint2: [%f] %s:%d\n", profiling_get_time(), __func__, __LINE__);
|
||||
if (systray.icons_per_column == 0 || systray.icons_per_row == 0)
|
||||
return;
|
||||
|
||||
@@ -301,7 +301,7 @@ void on_change_systray(void *obj)
|
||||
if (systray_profile)
|
||||
fprintf(stderr,
|
||||
"%s:%d win = %lu (%s), parent = %lu, x = %d, y = %d\n",
|
||||
__FUNCTION__,
|
||||
__func__,
|
||||
__LINE__,
|
||||
traywin->win,
|
||||
traywin->name,
|
||||
@@ -332,7 +332,7 @@ void on_change_systray(void *obj)
|
||||
unsigned int width, height, depth;
|
||||
Window root;
|
||||
if (!XGetGeometry(server.display, traywin->parent, &root, &xpos, &ypos, &width, &height, &border_width, &depth)) {
|
||||
fprintf(stderr, RED "Couldn't get geometry of window!" RESET "\n");
|
||||
fprintf(stderr, RED "tint2: Couldn't get geometry of window!" RESET "\n");
|
||||
}
|
||||
if (width != traywin->width || height != traywin->height || xpos != traywin->x || ypos != traywin->y) {
|
||||
if (systray_profile)
|
||||
@@ -358,7 +358,7 @@ void on_change_systray(void *obj)
|
||||
void start_net()
|
||||
{
|
||||
if (systray_profile)
|
||||
fprintf(stderr, "[%f] %s:%d\n", profiling_get_time(), __FUNCTION__, __LINE__);
|
||||
fprintf(stderr, "tint2: [%f] %s:%d\n", profiling_get_time(), __func__, __LINE__);
|
||||
if (net_sel_win) {
|
||||
// protocol already started
|
||||
if (!systray_enabled)
|
||||
@@ -395,11 +395,11 @@ void start_net()
|
||||
&bytes_after,
|
||||
&prop);
|
||||
|
||||
fprintf(stderr, RED "tint2 : another systray is running" RESET);
|
||||
fprintf(stderr, RED "tint2: another systray is running, cannot use systray" RESET);
|
||||
if (ret == Success && prop) {
|
||||
pid = prop[1] * 256;
|
||||
pid += prop[0];
|
||||
fprintf(stderr, " pid=%d", pid);
|
||||
fprintf(stderr, "tint2: pid=%d", pid);
|
||||
}
|
||||
fprintf(stderr, RESET "\n");
|
||||
return;
|
||||
@@ -407,7 +407,7 @@ void start_net()
|
||||
|
||||
// init systray protocol
|
||||
net_sel_win = XCreateSimpleWindow(server.display, server.root_win, -1, -1, 1, 1, 0, 0, 0);
|
||||
fprintf(stderr, "systray window %ld\n", net_sel_win);
|
||||
fprintf(stderr, "tint2: systray window %ld\n", net_sel_win);
|
||||
|
||||
// v0.3 trayer specification. tint2 always horizontal.
|
||||
// Vertical panel will draw the systray horizontal.
|
||||
@@ -458,13 +458,13 @@ void start_net()
|
||||
XSetSelectionOwner(server.display, server.atom._NET_SYSTEM_TRAY_SCREEN, net_sel_win, CurrentTime);
|
||||
if (XGetSelectionOwner(server.display, server.atom._NET_SYSTEM_TRAY_SCREEN) != net_sel_win) {
|
||||
stop_net();
|
||||
fprintf(stderr, RED "tint2 : can't get systray manager" RESET "\n");
|
||||
fprintf(stderr, RED "tint2: cannot find systray manager" RESET "\n");
|
||||
return;
|
||||
}
|
||||
|
||||
fprintf(stderr, GREEN "tint2 : systray started" RESET "\n");
|
||||
fprintf(stderr, GREEN "tint2: systray started" RESET "\n");
|
||||
if (systray_profile)
|
||||
fprintf(stderr, "[%f] %s:%d\n", profiling_get_time(), __FUNCTION__, __LINE__);
|
||||
fprintf(stderr, "tint2: [%f] %s:%d\n", profiling_get_time(), __func__, __LINE__);
|
||||
XClientMessageEvent ev;
|
||||
ev.type = ClientMessage;
|
||||
ev.window = server.root_win;
|
||||
@@ -478,10 +478,10 @@ void start_net()
|
||||
XSendEvent(server.display, server.root_win, False, StructureNotifyMask, (XEvent *)&ev);
|
||||
}
|
||||
|
||||
void net_message(XClientMessageEvent *e)
|
||||
void handle_systray_event(XClientMessageEvent *e)
|
||||
{
|
||||
if (systray_profile)
|
||||
fprintf(stderr, "[%f] %s:%d\n", profiling_get_time(), __FUNCTION__, __LINE__);
|
||||
fprintf(stderr, "tint2: [%f] %s:%d\n", profiling_get_time(), __func__, __LINE__);
|
||||
|
||||
Window win;
|
||||
unsigned long opcode = e->data.l[1];
|
||||
@@ -499,9 +499,9 @@ void net_message(XClientMessageEvent *e)
|
||||
|
||||
default:
|
||||
if (opcode == server.atom._NET_SYSTEM_TRAY_MESSAGE_DATA)
|
||||
fprintf(stderr, "message from dockapp: %s\n", e->data.b);
|
||||
fprintf(stderr, "tint2: message from dockapp: %s\n", e->data.b);
|
||||
else
|
||||
fprintf(stderr, RED "SYSTEM_TRAY : unknown message type" RESET "\n");
|
||||
fprintf(stderr, RED "tint2: SYSTEM_TRAY : unknown message type" RESET "\n");
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -509,7 +509,7 @@ void net_message(XClientMessageEvent *e)
|
||||
void stop_net()
|
||||
{
|
||||
if (systray_profile)
|
||||
fprintf(stderr, "[%f] %s:%d\n", profiling_get_time(), __FUNCTION__, __LINE__);
|
||||
fprintf(stderr, "tint2: [%f] %s:%d\n", profiling_get_time(), __func__, __LINE__);
|
||||
if (systray.list_icons) {
|
||||
// remove_icon change systray.list_icons
|
||||
while (systray.list_icons)
|
||||
@@ -529,10 +529,10 @@ gboolean error;
|
||||
int window_error_handler(Display *d, XErrorEvent *e)
|
||||
{
|
||||
if (systray_profile)
|
||||
fprintf(stderr, RED "[%f] %s:%d" RESET "\n", profiling_get_time(), __FUNCTION__, __LINE__);
|
||||
fprintf(stderr, RED "tint2: [%f] %s:%d" RESET "\n", profiling_get_time(), __func__, __LINE__);
|
||||
error = TRUE;
|
||||
if (e->error_code != BadWindow) {
|
||||
fprintf(stderr, RED "systray: error code %d" RESET "\n", e->error_code);
|
||||
fprintf(stderr, RED "tint2: systray: error code %d" RESET "\n", e->error_code);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
@@ -543,11 +543,11 @@ static gint compare_traywindows(gconstpointer a, gconstpointer b)
|
||||
const TrayWindow *traywin_b = (const TrayWindow *)b;
|
||||
|
||||
#if 0
|
||||
// This breaks pygtk2 StatusIcon with blinking activated
|
||||
if (traywin_a->empty && !traywin_b->empty)
|
||||
return 1 * (systray.sort == SYSTRAY_SORT_RIGHT2LEFT ? -1 : 1);
|
||||
if (!traywin_a->empty && traywin_b->empty)
|
||||
return -1 * (systray.sort == SYSTRAY_SORT_RIGHT2LEFT ? -1 : 1);
|
||||
// This breaks pygtk2 StatusIcon with blinking activated
|
||||
if (traywin_a->empty && !traywin_b->empty)
|
||||
return 1 * (systray.sort == SYSTRAY_SORT_RIGHT2LEFT ? -1 : 1);
|
||||
if (!traywin_a->empty && traywin_b->empty)
|
||||
return -1 * (systray.sort == SYSTRAY_SORT_RIGHT2LEFT ? -1 : 1);
|
||||
#endif
|
||||
|
||||
if (systray.sort == SYSTRAY_SORT_ASCENDING || systray.sort == SYSTRAY_SORT_DESCENDING) {
|
||||
@@ -564,18 +564,18 @@ static gint compare_traywindows(gconstpointer a, gconstpointer b)
|
||||
|
||||
void print_icons()
|
||||
{
|
||||
fprintf(stderr, "systray.list_icons: \n");
|
||||
fprintf(stderr, "tint2: systray.list_icons: \n");
|
||||
for (GSList *l = systray.list_icons; l; l = l->next) {
|
||||
TrayWindow *t = l->data;
|
||||
fprintf(stderr, "%s\n", t->name);
|
||||
fprintf(stderr, "tint2: %s\n", t->name);
|
||||
}
|
||||
fprintf(stderr, "systray.list_icons order: \n");
|
||||
fprintf(stderr, "tint2: systray.list_icons order: \n");
|
||||
for (GSList *l = systray.list_icons; l; l = l->next) {
|
||||
if (l->next) {
|
||||
TrayWindow *t = l->data;
|
||||
TrayWindow *u = l->next->data;
|
||||
int cmp = compare_traywindows(t, u);
|
||||
fprintf(stderr, "%s %s %s\n", t->name, cmp < 0 ? "<" : cmp == 0 ? "=" : ">", u->name);
|
||||
fprintf(stderr, "tint2: %s %s %s\n", t->name, cmp < 0 ? "<" : cmp == 0 ? "=" : ">", u->name);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -586,14 +586,14 @@ gboolean reject_icon(Window win)
|
||||
if (!systray_hide_name_regex) {
|
||||
systray_hide_name_regex = (regex_t *)calloc(1, sizeof(*systray_hide_name_regex));
|
||||
if (regcomp(systray_hide_name_regex, systray_hide_name_filter, 0) != 0) {
|
||||
fprintf(stderr, RED "Could not compile regex %s" RESET "\n", systray_hide_name_filter);
|
||||
fprintf(stderr, RED "tint2: Could not compile regex %s" RESET "\n", systray_hide_name_filter);
|
||||
free_and_null(systray_hide_name_regex);
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
char *name = get_window_name(win);
|
||||
if (regexec(systray_hide_name_regex, name, 0, NULL, 0) == 0) {
|
||||
fprintf(stderr, GREEN "Filtering out systray icon '%s'" RESET "\n", name);
|
||||
fprintf(stderr, GREEN "tint2: Filtering out systray icon '%s'" RESET "\n", name);
|
||||
return TRUE;
|
||||
}
|
||||
}
|
||||
@@ -623,7 +623,7 @@ gboolean add_icon(Window win)
|
||||
|
||||
char *name = get_window_name(win);
|
||||
if (systray_profile)
|
||||
fprintf(stderr, "[%f] %s:%d win = %lu (%s)\n", profiling_get_time(), __FUNCTION__, __LINE__, win, name);
|
||||
fprintf(stderr, "tint2: [%f] %s:%d win = %lu (%s)\n", profiling_get_time(), __func__, __LINE__, win, name);
|
||||
Panel *panel = systray.area.panel;
|
||||
|
||||
// Get the process ID of the application that created the window
|
||||
@@ -655,7 +655,7 @@ gboolean add_icon(Window win)
|
||||
// Create the parent window that will embed the icon
|
||||
XWindowAttributes attr;
|
||||
if (systray_profile)
|
||||
fprintf(stderr, "XGetWindowAttributes(server.display, win = %ld, &attr)\n", win);
|
||||
fprintf(stderr, "tint2: XGetWindowAttributes(server.display, win = %ld, &attr)\n", win);
|
||||
if (XGetWindowAttributes(server.display, win, &attr) == False) {
|
||||
free(name);
|
||||
XSelectInput(server.display, win, NoEventMask);
|
||||
@@ -679,7 +679,7 @@ gboolean add_icon(Window win)
|
||||
win,
|
||||
name,
|
||||
pid,
|
||||
attr.visual,
|
||||
(void*)attr.visual,
|
||||
attr.colormap,
|
||||
attr.depth,
|
||||
attr.width,
|
||||
@@ -706,7 +706,7 @@ gboolean add_icon(Window win)
|
||||
}
|
||||
|
||||
if (systray_profile)
|
||||
fprintf(stderr, "XCreateWindow(...)\n");
|
||||
fprintf(stderr, "tint2: XCreateWindow(...)\n");
|
||||
Window parent = XCreateWindow(server.display,
|
||||
panel->main_win,
|
||||
0,
|
||||
@@ -730,6 +730,8 @@ gboolean add_icon(Window win)
|
||||
traywin->pid = pid;
|
||||
traywin->name = name;
|
||||
traywin->chrono = chrono;
|
||||
INIT_TIMER(traywin->render_timer);
|
||||
INIT_TIMER(traywin->resize_timer);
|
||||
chrono++;
|
||||
|
||||
show(&systray.area);
|
||||
@@ -743,19 +745,19 @@ gboolean add_icon(Window win)
|
||||
|
||||
if (!panel->is_hidden) {
|
||||
if (systray_profile)
|
||||
fprintf(stderr, "XMapRaised(server.display, traywin->parent)\n");
|
||||
fprintf(stderr, "tint2: XMapRaised(server.display, traywin->parent)\n");
|
||||
XMapRaised(server.display, traywin->parent);
|
||||
}
|
||||
|
||||
if (systray_profile)
|
||||
fprintf(stderr, "[%f] %s:%d\n", profiling_get_time(), __FUNCTION__, __LINE__);
|
||||
fprintf(stderr, "tint2: [%f] %s:%d\n", profiling_get_time(), __func__, __LINE__);
|
||||
|
||||
// Resize and redraw the systray
|
||||
if (systray_profile)
|
||||
fprintf(stderr,
|
||||
BLUE "[%f] %s:%d trigger resize & redraw" RESET "\n",
|
||||
profiling_get_time(),
|
||||
__FUNCTION__,
|
||||
__func__,
|
||||
__LINE__);
|
||||
systray.area.resize_needed = TRUE;
|
||||
panel->area.resize_needed = TRUE;
|
||||
@@ -770,7 +772,7 @@ gboolean reparent_icon(TrayWindow *traywin)
|
||||
fprintf(stderr,
|
||||
"[%f] %s:%d win = %lu (%s)\n",
|
||||
profiling_get_time(),
|
||||
__FUNCTION__,
|
||||
__func__,
|
||||
__LINE__,
|
||||
traywin->win,
|
||||
traywin->name);
|
||||
@@ -808,7 +810,7 @@ gboolean reparent_icon(TrayWindow *traywin)
|
||||
e.xclient.data.l[3] = traywin->parent;
|
||||
e.xclient.data.l[4] = 0;
|
||||
if (systray_profile)
|
||||
fprintf(stderr, "XSendEvent(server.display, traywin->win, False, NoEventMask, &e)\n");
|
||||
fprintf(stderr, "tint2: XSendEvent(server.display, traywin->win, False, NoEventMask, &e)\n");
|
||||
XSendEvent(server.display, traywin->win, False, NoEventMask, &e);
|
||||
}
|
||||
|
||||
@@ -832,7 +834,7 @@ gboolean reparent_icon(TrayWindow *traywin)
|
||||
fprintf(stderr,
|
||||
"[%f] %s:%d win = %lu (%s)\n",
|
||||
profiling_get_time(),
|
||||
__FUNCTION__,
|
||||
__func__,
|
||||
__LINE__,
|
||||
traywin->win,
|
||||
traywin->name);
|
||||
@@ -846,7 +848,7 @@ gboolean embed_icon(TrayWindow *traywin)
|
||||
fprintf(stderr,
|
||||
"[%f] %s:%d win = %lu (%s)\n",
|
||||
profiling_get_time(),
|
||||
__FUNCTION__,
|
||||
__func__,
|
||||
__LINE__,
|
||||
traywin->win,
|
||||
traywin->name);
|
||||
@@ -859,62 +861,13 @@ gboolean embed_icon(TrayWindow *traywin)
|
||||
error = FALSE;
|
||||
XErrorHandler old = XSetErrorHandler(window_error_handler);
|
||||
|
||||
if (0) {
|
||||
Atom acttype;
|
||||
int actfmt;
|
||||
unsigned long nbitem, bytes;
|
||||
unsigned long *data = 0;
|
||||
int ret;
|
||||
|
||||
if (systray_profile)
|
||||
fprintf(stderr,
|
||||
"XGetWindowProperty(server.display, traywin->win, server.atom._XEMBED_INFO, 0, 2, False, "
|
||||
"server.atom._XEMBED_INFO, &acttype, &actfmt, &nbitem, &bytes, &data)\n");
|
||||
ret = XGetWindowProperty(server.display,
|
||||
traywin->win,
|
||||
server.atom._XEMBED_INFO,
|
||||
0,
|
||||
2,
|
||||
False,
|
||||
server.atom._XEMBED_INFO,
|
||||
&acttype,
|
||||
&actfmt,
|
||||
&nbitem,
|
||||
&bytes,
|
||||
(unsigned char **)&data);
|
||||
if (ret == Success) {
|
||||
if (data) {
|
||||
if (nbitem >= 2) {
|
||||
int hide = ((data[1] & XEMBED_MAPPED) == 0);
|
||||
if (hide) {
|
||||
// In theory we have to check the embedding with this and remove icons that refuse embedding.
|
||||
// In practice we have no idea when the other application processes the event and accepts the
|
||||
// embed so we cannot check now without a race.
|
||||
// Race can be triggered with PyGtk(2) apps.
|
||||
// We could defer this for later (if we set PropertyChangeMask in XSelectInput we get notified)
|
||||
// but for some reason it breaks transparency for Qt icons. So we don't.
|
||||
// fprintf(stderr, RED "tint2: window refused embedding" RESET "\n");
|
||||
// remove_icon(traywin);
|
||||
// XFree(data);
|
||||
// return FALSE;
|
||||
}
|
||||
}
|
||||
XFree(data);
|
||||
}
|
||||
} else {
|
||||
fprintf(stderr, RED "tint2 : xembed error" RESET "\n");
|
||||
remove_icon(traywin);
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
// Redirect rendering when using compositing
|
||||
if (systray_composited) {
|
||||
if (systray_profile)
|
||||
fprintf(stderr, "XDamageCreate(server.display, traywin->parent, XDamageReportRawRectangles)\n");
|
||||
fprintf(stderr, "tint2: XDamageCreate(server.display, traywin->parent, XDamageReportRawRectangles)\n");
|
||||
traywin->damage = XDamageCreate(server.display, traywin->parent, XDamageReportRawRectangles);
|
||||
if (systray_profile)
|
||||
fprintf(stderr, "XCompositeRedirectWindow(server.display, traywin->parent, CompositeRedirectManual)\n");
|
||||
fprintf(stderr, "tint2: XCompositeRedirectWindow(server.display, traywin->parent, CompositeRedirectManual)\n");
|
||||
XCompositeRedirectWindow(server.display, traywin->parent, CompositeRedirectManual);
|
||||
}
|
||||
|
||||
@@ -922,16 +875,16 @@ gboolean embed_icon(TrayWindow *traywin)
|
||||
|
||||
// Make the icon visible
|
||||
if (systray_profile)
|
||||
fprintf(stderr, "XMapWindow(server.display, traywin->win)\n");
|
||||
fprintf(stderr, "tint2: XMapWindow(server.display, traywin->win)\n");
|
||||
XMapWindow(server.display, traywin->win);
|
||||
if (!panel->is_hidden) {
|
||||
if (systray_profile)
|
||||
fprintf(stderr, "XMapRaised(server.display, traywin->parent)\n");
|
||||
fprintf(stderr, "tint2: XMapRaised(server.display, traywin->parent)\n");
|
||||
XMapRaised(server.display, traywin->parent);
|
||||
}
|
||||
|
||||
if (systray_profile)
|
||||
fprintf(stderr, "XSync(server.display, False)\n");
|
||||
fprintf(stderr, "tint2: XSync(server.display, False)\n");
|
||||
XSync(server.display, False);
|
||||
XSetErrorHandler(old);
|
||||
if (error != FALSE) {
|
||||
@@ -952,7 +905,7 @@ gboolean embed_icon(TrayWindow *traywin)
|
||||
fprintf(stderr,
|
||||
"[%f] %s:%d win = %lu (%s)\n",
|
||||
profiling_get_time(),
|
||||
__FUNCTION__,
|
||||
__func__,
|
||||
__LINE__,
|
||||
traywin->win,
|
||||
traywin->name);
|
||||
@@ -966,7 +919,7 @@ void remove_icon(TrayWindow *traywin)
|
||||
fprintf(stderr,
|
||||
"[%f] %s:%d win = %lu (%s)\n",
|
||||
profiling_get_time(),
|
||||
__FUNCTION__,
|
||||
__func__,
|
||||
__LINE__,
|
||||
traywin->win,
|
||||
traywin->name);
|
||||
@@ -974,7 +927,7 @@ void remove_icon(TrayWindow *traywin)
|
||||
|
||||
// remove from our list
|
||||
systray.list_icons = g_slist_remove(systray.list_icons, traywin);
|
||||
fprintf(stderr, YELLOW "remove_icon: %lu (%s)" RESET "\n", traywin->win, traywin->name);
|
||||
fprintf(stderr, YELLOW "tint2: remove_icon: %lu (%s)" RESET "\n", traywin->win, traywin->name);
|
||||
|
||||
XSelectInput(server.display, traywin->win, NoEventMask);
|
||||
if (traywin->damage)
|
||||
@@ -989,8 +942,8 @@ void remove_icon(TrayWindow *traywin)
|
||||
XDestroyWindow(server.display, traywin->parent);
|
||||
XSync(server.display, False);
|
||||
XSetErrorHandler(old);
|
||||
stop_timeout(traywin->render_timeout);
|
||||
stop_timeout(traywin->resize_timeout);
|
||||
destroy_timer(&traywin->render_timer);
|
||||
destroy_timer(&traywin->resize_timer);
|
||||
free(traywin->name);
|
||||
if (traywin->image) {
|
||||
imlib_context_set_image(traywin->image);
|
||||
@@ -1012,7 +965,7 @@ void remove_icon(TrayWindow *traywin)
|
||||
fprintf(stderr,
|
||||
BLUE "[%f] %s:%d trigger resize & redraw" RESET "\n",
|
||||
profiling_get_time(),
|
||||
__FUNCTION__,
|
||||
__func__,
|
||||
__LINE__);
|
||||
systray.area.resize_needed = TRUE;
|
||||
panel->area.resize_needed = TRUE;
|
||||
@@ -1104,9 +1057,8 @@ void systray_reconfigure_event(TrayWindow *traywin, XEvent *e)
|
||||
if (traywin->bad_size_counter < min_bad_resize_events) {
|
||||
systray_resize_icon(traywin);
|
||||
} else {
|
||||
if (!traywin->resize_timeout)
|
||||
traywin->resize_timeout =
|
||||
add_timeout(fast_resize_period, 0, systray_resize_icon, traywin, &traywin->resize_timeout);
|
||||
if (!traywin->resize_timer.enabled_)
|
||||
change_timer(&traywin->resize_timer, true, fast_resize_period, 0, systray_resize_icon, traywin);
|
||||
}
|
||||
} else {
|
||||
if (traywin->bad_size_counter == max_bad_resize_events) {
|
||||
@@ -1120,14 +1072,13 @@ void systray_reconfigure_event(TrayWindow *traywin, XEvent *e)
|
||||
// FIXME Normally we should force the icon to resize fill_color to the size we resized it to when we
|
||||
// embedded it.
|
||||
// However this triggers a resize loop in new versions of GTK, which we must avoid.
|
||||
if (!traywin->resize_timeout)
|
||||
traywin->resize_timeout =
|
||||
add_timeout(slow_resize_period, 0, systray_resize_icon, traywin, &traywin->resize_timeout);
|
||||
if (!traywin->resize_timer.enabled_)
|
||||
change_timer(&traywin->resize_timer, true, slow_resize_period, 0, systray_resize_icon, traywin);
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
// Correct size
|
||||
stop_timeout(traywin->resize_timeout);
|
||||
stop_timer(&traywin->resize_timer);
|
||||
}
|
||||
|
||||
// Resize and redraw the systray
|
||||
@@ -1135,7 +1086,7 @@ void systray_reconfigure_event(TrayWindow *traywin, XEvent *e)
|
||||
fprintf(stderr,
|
||||
BLUE "[%f] %s:%d trigger resize & redraw" RESET "\n",
|
||||
profiling_get_time(),
|
||||
__FUNCTION__,
|
||||
__func__,
|
||||
__LINE__);
|
||||
schedule_panel_redraw();
|
||||
refresh_systray = TRUE;
|
||||
@@ -1184,9 +1135,8 @@ void systray_resize_request_event(TrayWindow *traywin, XEvent *e)
|
||||
if (traywin->bad_size_counter < min_bad_resize_events) {
|
||||
systray_resize_icon(traywin);
|
||||
} else {
|
||||
if (!traywin->resize_timeout)
|
||||
traywin->resize_timeout =
|
||||
add_timeout(fast_resize_period, 0, systray_resize_icon, traywin, &traywin->resize_timeout);
|
||||
if (!traywin->resize_timer.enabled_)
|
||||
change_timer(&traywin->resize_timer, true, fast_resize_period, 0, systray_resize_icon, traywin);
|
||||
}
|
||||
} else {
|
||||
if (traywin->bad_size_counter == max_bad_resize_events) {
|
||||
@@ -1199,14 +1149,13 @@ void systray_resize_request_event(TrayWindow *traywin, XEvent *e)
|
||||
// Delayed resize
|
||||
// FIXME Normally we should force the icon to resize to the size we resized it to when we embedded it.
|
||||
// However this triggers a resize loop in some versions of GTK, which we must avoid.
|
||||
if (!traywin->resize_timeout)
|
||||
traywin->resize_timeout =
|
||||
add_timeout(slow_resize_period, 0, systray_resize_icon, traywin, &traywin->resize_timeout);
|
||||
if (!traywin->resize_timer.enabled_)
|
||||
change_timer(&traywin->resize_timer, true, slow_resize_period, 0, systray_resize_icon, traywin);
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
// Correct size
|
||||
stop_timeout(traywin->resize_timeout);
|
||||
stop_timer(&traywin->resize_timer);
|
||||
}
|
||||
|
||||
// Resize and redraw the systray
|
||||
@@ -1214,7 +1163,7 @@ void systray_resize_request_event(TrayWindow *traywin, XEvent *e)
|
||||
fprintf(stderr,
|
||||
BLUE "[%f] %s:%d trigger resize & redraw" RESET "\n",
|
||||
profiling_get_time(),
|
||||
__FUNCTION__,
|
||||
__func__,
|
||||
__LINE__);
|
||||
schedule_panel_redraw();
|
||||
refresh_systray = TRUE;
|
||||
@@ -1226,7 +1175,7 @@ void systray_destroy_event(TrayWindow *traywin)
|
||||
fprintf(stderr,
|
||||
"[%f] %s:%d win = %lu (%s)\n",
|
||||
profiling_get_time(),
|
||||
__FUNCTION__,
|
||||
__func__,
|
||||
__LINE__,
|
||||
traywin->win,
|
||||
traywin->name);
|
||||
@@ -1261,7 +1210,7 @@ void systray_render_icon_composited(void *t)
|
||||
fprintf(stderr,
|
||||
"[%f] %s:%d win = %lu (%s)\n",
|
||||
profiling_get_time(),
|
||||
__FUNCTION__,
|
||||
__func__,
|
||||
__LINE__,
|
||||
traywin->win,
|
||||
traywin->name);
|
||||
@@ -1273,13 +1222,12 @@ void systray_render_icon_composited(void *t)
|
||||
if (compare_timespecs(&earliest_render, &now) > 0) {
|
||||
traywin->num_fast_renders++;
|
||||
if (traywin->num_fast_renders > max_fast_refreshes) {
|
||||
traywin->render_timeout =
|
||||
add_timeout(min_refresh_period, 0, systray_render_icon_composited, traywin, &traywin->render_timeout);
|
||||
change_timer(&traywin->render_timer, true, min_refresh_period, 0, systray_render_icon_composited, traywin);
|
||||
if (systray_profile)
|
||||
fprintf(stderr,
|
||||
YELLOW "[%f] %s:%d win = %lu (%s) delaying rendering" RESET "\n",
|
||||
profiling_get_time(),
|
||||
__FUNCTION__,
|
||||
__func__,
|
||||
__LINE__,
|
||||
traywin->win,
|
||||
traywin->name);
|
||||
@@ -1293,23 +1241,19 @@ void systray_render_icon_composited(void *t)
|
||||
|
||||
if (traywin->width == 0 || traywin->height == 0) {
|
||||
// reschedule rendering since the geometry information has not yet been processed (can happen on slow cpu)
|
||||
traywin->render_timeout =
|
||||
add_timeout(min_refresh_period, 0, systray_render_icon_composited, traywin, &traywin->render_timeout);
|
||||
change_timer(&traywin->render_timer, true, min_refresh_period, 0, systray_render_icon_composited, traywin);
|
||||
if (systray_profile)
|
||||
fprintf(stderr,
|
||||
YELLOW "[%f] %s:%d win = %lu (%s) delaying rendering" RESET "\n",
|
||||
profiling_get_time(),
|
||||
__FUNCTION__,
|
||||
__func__,
|
||||
__LINE__,
|
||||
traywin->win,
|
||||
traywin->name);
|
||||
return;
|
||||
}
|
||||
|
||||
if (traywin->render_timeout) {
|
||||
stop_timeout(traywin->render_timeout);
|
||||
traywin->render_timeout = NULL;
|
||||
}
|
||||
stop_timer(&traywin->render_timer);
|
||||
|
||||
// good systray icons support 32 bit depth, but some icons are still 24 bit.
|
||||
// We create a heuristic mask for these icons, i.e. we get the rgb value in the top left corner, and
|
||||
@@ -1328,7 +1272,7 @@ void systray_render_icon_composited(void *t)
|
||||
} else if (traywin->depth == 32) {
|
||||
f = XRenderFindStandardFormat(server.display, PictStandardARGB32);
|
||||
} else {
|
||||
fprintf(stderr, RED "Strange tray icon found with depth: %d" RESET "\n", traywin->depth);
|
||||
fprintf(stderr, RED "tint2: Strange tray icon found with depth: %d" RESET "\n", traywin->depth);
|
||||
XFreePixmap(server.display, tmp_pmap);
|
||||
return;
|
||||
}
|
||||
@@ -1430,7 +1374,7 @@ void systray_render_icon_composited(void *t)
|
||||
fprintf(stderr,
|
||||
"[%f] %s:%d win = %lu (%s)\n",
|
||||
profiling_get_time(),
|
||||
__FUNCTION__,
|
||||
__func__,
|
||||
__LINE__,
|
||||
traywin->win,
|
||||
traywin->name);
|
||||
@@ -1468,13 +1412,11 @@ void systray_render_icon(void *t)
|
||||
// fprintf(stderr,
|
||||
// YELLOW "[%f] %s:%d win = %lu (%s) delaying rendering" RESET "\n",
|
||||
// profiling_get_time(),
|
||||
// __FUNCTION__,
|
||||
// __func__,
|
||||
// __LINE__,
|
||||
// traywin->win,
|
||||
// traywin->name);
|
||||
stop_timeout(traywin->render_timeout);
|
||||
traywin->render_timeout =
|
||||
add_timeout(min_refresh_period, 0, systray_render_icon, traywin, &traywin->render_timeout);
|
||||
change_timer(&traywin->render_timer, true, min_refresh_period, 0, systray_render_icon, traywin);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -1482,7 +1424,7 @@ void systray_render_icon(void *t)
|
||||
fprintf(stderr,
|
||||
"[%f] %s:%d win = %lu (%s)\n",
|
||||
profiling_get_time(),
|
||||
__FUNCTION__,
|
||||
__func__,
|
||||
__LINE__,
|
||||
traywin->win,
|
||||
traywin->name);
|
||||
@@ -1497,25 +1439,19 @@ void systray_render_icon(void *t)
|
||||
unsigned int width, height, depth;
|
||||
Window root;
|
||||
if (!XGetGeometry(server.display, traywin->win, &root, &xpos, &ypos, &width, &height, &border_width, &depth)) {
|
||||
stop_timeout(traywin->render_timeout);
|
||||
if (!traywin->resize_timeout)
|
||||
traywin->render_timeout =
|
||||
add_timeout(min_refresh_period, 0, systray_render_icon, traywin, &traywin->render_timeout);
|
||||
change_timer(&traywin->render_timer, true, min_refresh_period, 0, systray_render_icon, traywin);
|
||||
systray_render_icon_from_image(traywin);
|
||||
XSetErrorHandler(old);
|
||||
return;
|
||||
} else {
|
||||
if (xpos != 0 || ypos != 0 || width != traywin->width || height != traywin->height) {
|
||||
stop_timeout(traywin->render_timeout);
|
||||
if (!traywin->resize_timeout)
|
||||
traywin->render_timeout =
|
||||
add_timeout(min_refresh_period, 0, systray_render_icon, traywin, &traywin->render_timeout);
|
||||
change_timer(&traywin->render_timer, true, min_refresh_period, 0, systray_render_icon, traywin);
|
||||
systray_render_icon_from_image(traywin);
|
||||
if (systray_profile)
|
||||
fprintf(stderr,
|
||||
YELLOW "[%f] %s:%d win = %lu (%s) delaying rendering" RESET "\n",
|
||||
profiling_get_time(),
|
||||
__FUNCTION__,
|
||||
__func__,
|
||||
__LINE__,
|
||||
traywin->win,
|
||||
traywin->name);
|
||||
@@ -1527,7 +1463,7 @@ void systray_render_icon(void *t)
|
||||
}
|
||||
|
||||
if (systray_profile)
|
||||
fprintf(stderr, "rendering tray icon\n");
|
||||
fprintf(stderr, "tint2: rendering tray icon\n");
|
||||
|
||||
if (systray_composited) {
|
||||
systray_render_icon_composited(traywin);
|
||||
@@ -1549,7 +1485,7 @@ void systray_render_icon(void *t)
|
||||
void refresh_systray_icons()
|
||||
{
|
||||
if (systray_profile)
|
||||
fprintf(stderr, BLUE "[%f] %s:%d" RESET "\n", profiling_get_time(), __FUNCTION__, __LINE__);
|
||||
fprintf(stderr, BLUE "tint2: [%f] %s:%d" RESET "\n", profiling_get_time(), __func__, __LINE__);
|
||||
TrayWindow *traywin;
|
||||
GSList *l;
|
||||
for (l = systray.list_icons; l; l = l->next) {
|
||||
|
||||
@@ -56,11 +56,11 @@ typedef struct {
|
||||
// Members used for rendering
|
||||
struct timespec time_last_render;
|
||||
int num_fast_renders;
|
||||
timeout *render_timeout;
|
||||
Timer render_timer;
|
||||
// Members used for resizing
|
||||
int bad_size_counter;
|
||||
struct timespec time_last_resize;
|
||||
timeout *resize_timeout;
|
||||
Timer resize_timer;
|
||||
// Icon contents if we are compositing the icon, otherwise null
|
||||
Imlib_Image image;
|
||||
// XDamage
|
||||
@@ -96,7 +96,7 @@ gboolean systray_on_monitor(int i_monitor, int num_panels);
|
||||
// many tray icon doesn't manage stop/restart of the systray manager
|
||||
void start_net();
|
||||
void stop_net();
|
||||
void net_message(XClientMessageEvent *e);
|
||||
void handle_systray_event(XClientMessageEvent *e);
|
||||
|
||||
gboolean add_icon(Window id);
|
||||
gboolean reparent_icon(TrayWindow *traywin);
|
||||
|
||||
@@ -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,10 +107,6 @@ Task *add_task(Window win)
|
||||
(int)win,
|
||||
task_template.title ? task_template.title : "null");
|
||||
|
||||
// fprintf(stderr, "%s %d: win = %ld, task = %s\n", __FUNCTION__, __LINE__, win, task_template.title ?
|
||||
// task_template.title : "??");
|
||||
// fprintf(stderr, "new task %s win %u: desktop %d, monitor %d\n", new_task.title, win, new_task.desktop, monitor);
|
||||
|
||||
GPtrArray *task_buttons = g_ptr_array_new();
|
||||
for (int j = 0; j < panels[monitor].num_desktops; j++) {
|
||||
if (task_template.desktop != ALL_DESKTOPS && task_template.desktop != j)
|
||||
@@ -110,6 +120,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 +129,16 @@ Task *add_task(Window win)
|
||||
task_instance->win_h = task_template.win_h;
|
||||
task_instance->current_state = TASK_UNDEFINED; // to update the current state later in set_task_state...
|
||||
if (task_instance->desktop == ALL_DESKTOPS && server.desktop != j) {
|
||||
// fprintf(stderr, "%s %d: win = %ld hiding task: another desktop\n", __FUNCTION__, __LINE__, win);
|
||||
task_instance->area.on_screen = always_show_all_desktop_tasks;
|
||||
}
|
||||
task_instance->title = task_template.title;
|
||||
if (panels[monitor].g_task.tooltip_enabled)
|
||||
if (panels[monitor].g_task.tooltip_enabled) {
|
||||
task_instance->area._get_tooltip_text = task_get_tooltip;
|
||||
task_instance->area._get_tooltip_image = task_get_thumbnail;
|
||||
}
|
||||
task_instance->icon_color = task_template.icon_color;
|
||||
task_instance->icon_color_hover = task_template.icon_color_hover;
|
||||
task_instance->icon_color_press = task_template.icon_color_press;
|
||||
for (int k = 0; k < TASK_STATE_COUNT; ++k) {
|
||||
task_instance->icon[k] = task_template.icon[k];
|
||||
task_instance->icon_hover[k] = task_template.icon_hover[k];
|
||||
@@ -158,26 +173,10 @@ Task *add_task(Window win)
|
||||
return (Task *)g_ptr_array_index(task_buttons, 0);
|
||||
}
|
||||
|
||||
void remove_task(Task *task)
|
||||
void task_remove_icon(Task *task)
|
||||
{
|
||||
if (!task)
|
||||
return;
|
||||
|
||||
// fprintf(stderr, "%s %d: win = %ld, task = %s\n", __FUNCTION__, __LINE__, task->win, task->title ? task->title :
|
||||
// "??");
|
||||
|
||||
if (taskbar_mode == MULTI_DESKTOP) {
|
||||
Panel *panel = task->area.panel;
|
||||
panel->area.resize_needed = 1;
|
||||
}
|
||||
|
||||
Window win = task->win;
|
||||
|
||||
// free title and icon just for the first task
|
||||
// even with task_on_all_desktop and with task_on_all_panel
|
||||
// printf("remove_task %s %d\n", task->title, task->desktop);
|
||||
if (task->title)
|
||||
free(task->title);
|
||||
for (int k = 0; k < TASK_STATE_COUNT; ++k) {
|
||||
if (task->icon[k]) {
|
||||
imlib_context_set_image(task->icon[k]);
|
||||
@@ -195,6 +194,27 @@ void remove_task(Task *task)
|
||||
task->icon_press[k] = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void remove_task(Task *task)
|
||||
{
|
||||
if (!task)
|
||||
return;
|
||||
|
||||
if (taskbar_mode == MULTI_DESKTOP) {
|
||||
Panel *panel = task->area.panel;
|
||||
panel->area.resize_needed = 1;
|
||||
}
|
||||
|
||||
Window win = task->win;
|
||||
|
||||
// free title and icon just for the first task
|
||||
// even with task_on_all_desktop and with task_on_all_panel
|
||||
if (task->title)
|
||||
free(task->title);
|
||||
if (task->thumbnail)
|
||||
cairo_surface_destroy(task->thumbnail);
|
||||
task_remove_icon(task);
|
||||
|
||||
GPtrArray *task_buttons = g_hash_table_lookup(win_to_task, &win);
|
||||
for (int i = 0; i < task_buttons->len; ++i) {
|
||||
@@ -205,6 +225,8 @@ void remove_task(Task *task)
|
||||
task_drag = 0;
|
||||
if (g_slist_find(urgent_list, task2))
|
||||
del_urgent(task2);
|
||||
if (g_tooltip.area == &task2->area)
|
||||
tooltip_hide(NULL);
|
||||
remove_area((Area *)task2);
|
||||
free(task2);
|
||||
}
|
||||
@@ -259,48 +281,31 @@ gboolean task_update_title(Task *task)
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
void task_update_icon(Task *task)
|
||||
Imlib_Image task_get_icon(Window win, int icon_size)
|
||||
{
|
||||
Panel *panel = task->area.panel;
|
||||
if (!panel->g_task.has_icon)
|
||||
return;
|
||||
|
||||
for (int k = 0; k < TASK_STATE_COUNT; ++k) {
|
||||
if (task->icon[k]) {
|
||||
imlib_context_set_image(task->icon[k]);
|
||||
imlib_free_image();
|
||||
task->icon[k] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
Imlib_Image img = NULL;
|
||||
|
||||
if (!img) {
|
||||
int len;
|
||||
gulong *data = server_get_property(task->win, server.atom._NET_WM_ICON, XA_CARDINAL, &len);
|
||||
if (data && 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);
|
||||
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",
|
||||
__FUNCTION__,
|
||||
w,
|
||||
h,
|
||||
task->title ? task->title : "task");
|
||||
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, icon_size);
|
||||
if (tmp_data) {
|
||||
DATA32 icon_data[w * h];
|
||||
for (int j = 0; j < w * h; ++j)
|
||||
icon_data[j] = tmp_data[j];
|
||||
img = imlib_create_image_using_copied_data(w, h, icon_data);
|
||||
}
|
||||
}
|
||||
XFree(data);
|
||||
}
|
||||
}
|
||||
|
||||
if (!img) {
|
||||
XWMHints *hints = XGetWMHints(server.display, task->win);
|
||||
XWMHints *hints = XGetWMHints(server.display, win);
|
||||
if (hints) {
|
||||
if (hints->flags & IconPixmapHint && hints->icon_pixmap != 0) {
|
||||
// get width, height and depth for the pixmap
|
||||
@@ -312,13 +317,6 @@ void task_update_icon(Task *task)
|
||||
XGetGeometry(server.display, hints->icon_pixmap, &root, &icon_x, &icon_y, &w, &h, &border_width, &bpp);
|
||||
imlib_context_set_drawable(hints->icon_pixmap);
|
||||
img = imlib_create_image_from_drawable(hints->icon_mask, 0, 0, w, h, 0);
|
||||
if (0 && img)
|
||||
fprintf(stderr,
|
||||
"%s: Got %dx%d pixmap icon via WM_HINTS for %s\n",
|
||||
__FUNCTION__,
|
||||
w,
|
||||
h,
|
||||
task->title ? task->title : "task");
|
||||
}
|
||||
XFree(hints);
|
||||
}
|
||||
@@ -327,10 +325,46 @@ void task_update_icon(Task *task)
|
||||
if (img == NULL) {
|
||||
imlib_context_set_image(default_icon);
|
||||
img = imlib_clone_image();
|
||||
if (0)
|
||||
fprintf(stderr, "%s: Using default icon for %s\n", __FUNCTION__, task->title ? task->title : "task");
|
||||
}
|
||||
|
||||
return img;
|
||||
}
|
||||
|
||||
void task_set_icon_color(Task *task, Imlib_Image icon)
|
||||
{
|
||||
get_image_mean_color(icon, &task->icon_color);
|
||||
if (panel_config.mouse_effects) {
|
||||
task->icon_color_hover = task->icon_color;
|
||||
adjust_color(&task->icon_color_hover,
|
||||
panel_config.mouse_over_alpha,
|
||||
panel_config.mouse_over_saturation,
|
||||
panel_config.mouse_over_brightness);
|
||||
task->icon_color_press = task->icon_color;
|
||||
adjust_color(&task->icon_color_press,
|
||||
panel_config.mouse_pressed_alpha,
|
||||
panel_config.mouse_pressed_saturation,
|
||||
panel_config.mouse_pressed_brightness);
|
||||
}
|
||||
}
|
||||
|
||||
void task_update_icon(Task *task)
|
||||
{
|
||||
Panel *panel = task->area.panel;
|
||||
if (!panel->g_task.has_icon) {
|
||||
if (panel_config.g_task.has_content_tint) {
|
||||
Imlib_Image img = task_get_icon(task->win, panel->g_task.icon_size1);
|
||||
task_set_icon_color(task, img);
|
||||
imlib_context_set_image(img);
|
||||
imlib_free_image();
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
task_remove_icon(task);
|
||||
|
||||
Imlib_Image img = task_get_icon(task->win, panel->g_task.icon_size1);
|
||||
task_set_icon_color(task, img);
|
||||
|
||||
// transform icons
|
||||
imlib_context_set_image(img);
|
||||
imlib_image_set_has_alpha(1);
|
||||
@@ -344,20 +378,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] != 0);
|
||||
if (panel_config.mouse_effects) {
|
||||
task->icon_hover[k] = adjust_icon(task->icon[k],
|
||||
panel_config.mouse_over_alpha,
|
||||
@@ -375,9 +399,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];
|
||||
@@ -436,7 +463,7 @@ void draw_task(void *obj, cairo_t *c)
|
||||
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 + 1) * 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);
|
||||
@@ -465,7 +492,7 @@ void task_dump_geometry(void *obj, int indent)
|
||||
Panel *panel = (Panel *)task->area.panel;
|
||||
|
||||
fprintf(stderr,
|
||||
"%*sText: x = %d, y = %d, w = %d, h = %d, align = %s, text = %s\n",
|
||||
"tint2: %*sText: x = %d, y = %d, w = %d, h = %d, align = %s, text = %s\n",
|
||||
indent,
|
||||
"",
|
||||
(int)panel->g_task.text_posx,
|
||||
@@ -475,7 +502,7 @@ void task_dump_geometry(void *obj, int indent)
|
||||
panel->g_task.centered ? "center" : "left",
|
||||
task->title);
|
||||
fprintf(stderr,
|
||||
"%*sIcon: x = %d, y = %d, w = h = %d\n",
|
||||
"tint2: %*sIcon: x = %d, y = %d, w = h = %d\n",
|
||||
indent,
|
||||
"",
|
||||
task->_icon_x,
|
||||
@@ -483,6 +510,24 @@ 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;
|
||||
@@ -590,7 +635,6 @@ void reset_active_task()
|
||||
}
|
||||
|
||||
Window w1 = get_active_window();
|
||||
// printf("Change active task %ld\n", w1);
|
||||
|
||||
if (w1) {
|
||||
if (!get_task_buttons(w1)) {
|
||||
@@ -602,11 +646,49 @@ 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;
|
||||
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);
|
||||
if (!thumbnail)
|
||||
return;
|
||||
if (task->thumbnail)
|
||||
cairo_surface_destroy(task->thumbnail);
|
||||
task->thumbnail = thumbnail;
|
||||
task->thumbnail_last_update = get_time();
|
||||
if (debug_thumbnails)
|
||||
fprintf(stderr,
|
||||
YELLOW "tint2: %s took %f ms (window: %s)" RESET "\n",
|
||||
__func__,
|
||||
1000 * (task->thumbnail_last_update - now),
|
||||
task->title ? task->title : "");
|
||||
if (g_tooltip.mapped && (g_tooltip.area == &task->area)) {
|
||||
tooltip_update_contents_for(&task->area);
|
||||
tooltip_update();
|
||||
}
|
||||
}
|
||||
|
||||
void set_task_state(Task *task, TaskState state)
|
||||
{
|
||||
if (!task || state == TASK_UNDEFINED || state >= TASK_STATE_COUNT)
|
||||
return;
|
||||
|
||||
if (!task->thumbnail)
|
||||
task_refresh_thumbnail(task);
|
||||
if (state == TASK_ACTIVE) {
|
||||
// For active windows, we get the thumbnail twice with a small delay in between.
|
||||
// This is because they sometimes redraw their windows slowly.
|
||||
taskbar_start_thumbnail_timer(THUMB_MODE_ACTIVE_WINDOW);
|
||||
}
|
||||
|
||||
if (state == TASK_ACTIVE && task->current_state != state) {
|
||||
clock_gettime(CLOCK_MONOTONIC, &task->last_activation_time);
|
||||
if (taskbar_sort_method == TASKBAR_SORT_LRU || taskbar_sort_method == TASKBAR_SORT_MRU) {
|
||||
@@ -621,7 +703,7 @@ void set_task_state(Task *task, TaskState state)
|
||||
}
|
||||
}
|
||||
|
||||
if (task->current_state != state || hide_task_diff_monitor) {
|
||||
if (task->current_state != state || hide_task_diff_monitor || hide_task_diff_desktop) {
|
||||
GPtrArray *task_buttons = get_task_buttons(task->win);
|
||||
if (task_buttons) {
|
||||
for (int i = 0; i < task_buttons->len; ++i) {
|
||||
@@ -645,6 +727,10 @@ void set_task_state(Task *task, TaskState state)
|
||||
hide = TRUE;
|
||||
}
|
||||
}
|
||||
if (hide_task_diff_desktop) {
|
||||
if (taskbar->desktop != server.desktop)
|
||||
hide = TRUE;
|
||||
}
|
||||
if (get_window_monitor(task->win) != ((Panel *)task->area.panel)->monitor &&
|
||||
(hide_task_diff_monitor || num_panels > 1)) {
|
||||
hide = TRUE;
|
||||
@@ -696,8 +782,8 @@ void add_urgent(Task *task)
|
||||
// not yet in the list, so we have to add it
|
||||
urgent_list = g_slist_prepend(urgent_list, task);
|
||||
|
||||
if (!urgent_timeout)
|
||||
urgent_timeout = add_timeout(10, 1000, blink_urgent, 0, &urgent_timeout);
|
||||
if (!urgent_timer.enabled_)
|
||||
change_timer(&urgent_timer, true, 10, 1000, blink_urgent, 0);
|
||||
|
||||
Panel *panel = task->area.panel;
|
||||
if (panel->is_hidden)
|
||||
@@ -707,8 +793,78 @@ void add_urgent(Task *task)
|
||||
void del_urgent(Task *task)
|
||||
{
|
||||
urgent_list = g_slist_remove(urgent_list, task);
|
||||
if (!urgent_list) {
|
||||
stop_timeout(urgent_timeout);
|
||||
urgent_timeout = NULL;
|
||||
if (!urgent_list)
|
||||
stop_timer(&urgent_timer);
|
||||
}
|
||||
|
||||
void task_handle_mouse_event(Task *task, MouseAction action)
|
||||
{
|
||||
if (!task)
|
||||
return;
|
||||
switch (action) {
|
||||
case NONE:
|
||||
break;
|
||||
case CLOSE:
|
||||
close_window(task->win);
|
||||
break;
|
||||
case TOGGLE:
|
||||
activate_window(task->win);
|
||||
break;
|
||||
case ICONIFY:
|
||||
XIconifyWindow(server.display, task->win, server.screen);
|
||||
break;
|
||||
case TOGGLE_ICONIFY:
|
||||
if (active_task && task->win == active_task->win)
|
||||
XIconifyWindow(server.display, task->win, server.screen);
|
||||
else
|
||||
activate_window(task->win);
|
||||
break;
|
||||
case SHADE:
|
||||
toggle_window_shade(task->win);
|
||||
break;
|
||||
case MAXIMIZE_RESTORE:
|
||||
toggle_window_maximized(task->win);
|
||||
break;
|
||||
case MAXIMIZE:
|
||||
toggle_window_maximized(task->win);
|
||||
break;
|
||||
case RESTORE:
|
||||
toggle_window_maximized(task->win);
|
||||
break;
|
||||
case DESKTOP_LEFT: {
|
||||
if (task->desktop == 0)
|
||||
break;
|
||||
int desktop = task->desktop - 1;
|
||||
change_window_desktop(task->win, desktop);
|
||||
if (desktop == server.desktop)
|
||||
activate_window(task->win);
|
||||
break;
|
||||
}
|
||||
case DESKTOP_RIGHT: {
|
||||
if (task->desktop == server.num_desktops)
|
||||
break;
|
||||
int desktop = task->desktop + 1;
|
||||
change_window_desktop(task->win, desktop);
|
||||
if (desktop == server.desktop)
|
||||
activate_window(task->win);
|
||||
break;
|
||||
}
|
||||
case NEXT_TASK: {
|
||||
Task *task1 = next_task(find_active_task(task));
|
||||
activate_window(task1->win);
|
||||
} break;
|
||||
case PREV_TASK: {
|
||||
Task *task1 = prev_task(find_active_task(task));
|
||||
activate_window(task1->win);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void task_update_desktop(Task *task)
|
||||
{
|
||||
Window win = task->win;
|
||||
remove_task(task);
|
||||
task = add_task(win);
|
||||
reset_active_task();
|
||||
schedule_panel_redraw();
|
||||
}
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
#include <X11/Xlib.h>
|
||||
#include <pango/pangocairo.h>
|
||||
#include <Imlib2.h>
|
||||
|
||||
#include "common.h"
|
||||
#include "timer.h"
|
||||
|
||||
@@ -41,10 +42,13 @@ typedef struct GlobalTask {
|
||||
// starting position for text ~ task_padding + task_border + icon_size
|
||||
double text_posx, text_height;
|
||||
gboolean has_font;
|
||||
gboolean has_content_tint;
|
||||
PangoFontDescription *font_desc;
|
||||
Color font[TASK_STATE_COUNT];
|
||||
int config_font_mask;
|
||||
gboolean tooltip_enabled;
|
||||
gboolean thumbnail_enabled;
|
||||
int thumbnail_width;
|
||||
} GlobalTask;
|
||||
|
||||
// Stores information about a task.
|
||||
@@ -60,6 +64,9 @@ 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;
|
||||
int urgent_tick;
|
||||
// These may not be up-to-date
|
||||
@@ -73,9 +80,11 @@ typedef struct Task {
|
||||
double _text_posy;
|
||||
int _icon_x;
|
||||
int _icon_y;
|
||||
cairo_surface_t *thumbnail;
|
||||
double thumbnail_last_update;
|
||||
} Task;
|
||||
|
||||
extern timeout *urgent_timeout;
|
||||
extern Timer urgent_timer;
|
||||
extern GSList *urgent_list;
|
||||
|
||||
Task *add_task(Window win);
|
||||
@@ -85,9 +94,12 @@ void draw_task(void *obj, cairo_t *c);
|
||||
void on_change_task(void *obj);
|
||||
|
||||
void task_update_icon(Task *task);
|
||||
void task_update_desktop(Task *task);
|
||||
gboolean task_update_title(Task *task);
|
||||
void reset_active_task();
|
||||
void set_task_state(Task *task, TaskState state);
|
||||
void task_handle_mouse_event(Task *task, MouseAction action);
|
||||
void task_refresh_thumbnail(Task *task);
|
||||
|
||||
// Given a pointer to the task that is currently under the mouse (current_task),
|
||||
// returns a pointer to the Task for the active window on the same taskbar.
|
||||
|
||||
@@ -32,6 +32,7 @@
|
||||
#include "window.h"
|
||||
#include "panel.h"
|
||||
#include "strnatcmp.h"
|
||||
#include "tooltip.h"
|
||||
|
||||
GHashTable *win_to_task;
|
||||
|
||||
@@ -39,12 +40,19 @@ Task *active_task;
|
||||
Task *task_drag;
|
||||
gboolean taskbar_enabled;
|
||||
gboolean taskbar_distribute_size;
|
||||
gboolean hide_task_diff_desktop;
|
||||
gboolean hide_inactive_tasks;
|
||||
gboolean hide_task_diff_monitor;
|
||||
gboolean hide_taskbar_if_empty;
|
||||
gboolean always_show_all_desktop_tasks;
|
||||
TaskbarSortMethod taskbar_sort_method;
|
||||
Alignment taskbar_alignment;
|
||||
static Timer thumbnail_update_timer_all;
|
||||
static Timer thumbnail_update_timer_active;
|
||||
static Timer thumbnail_update_timer_tooltip;
|
||||
|
||||
static GList *taskbar_task_orderings = NULL;
|
||||
static GList *taskbar_thumbnail_jobs_done = NULL;
|
||||
|
||||
void taskbar_init_fonts();
|
||||
int taskbar_compute_desired_size(void *obj);
|
||||
@@ -52,6 +60,8 @@ int taskbar_compute_desired_size(void *obj);
|
||||
// Removes the task with &win = key. The other args are ignored.
|
||||
void taskbar_remove_task(Window *win);
|
||||
|
||||
void taskbar_update_thumbnails(void *arg);
|
||||
|
||||
guint win_hash(gconstpointer key)
|
||||
{
|
||||
return *((const Window *)key);
|
||||
@@ -70,21 +80,61 @@ void free_ptr_array(gpointer data)
|
||||
void default_taskbar()
|
||||
{
|
||||
win_to_task = NULL;
|
||||
urgent_timeout = NULL;
|
||||
urgent_list = NULL;
|
||||
taskbar_enabled = FALSE;
|
||||
taskbar_distribute_size = FALSE;
|
||||
hide_task_diff_desktop = FALSE;
|
||||
hide_inactive_tasks = FALSE;
|
||||
hide_task_diff_monitor = FALSE;
|
||||
hide_taskbar_if_empty = FALSE;
|
||||
always_show_all_desktop_tasks = FALSE;
|
||||
taskbar_thumbnail_jobs_done = NULL;
|
||||
taskbar_sort_method = TASKBAR_NOSORT;
|
||||
taskbar_alignment = ALIGN_LEFT;
|
||||
default_taskbarname();
|
||||
}
|
||||
|
||||
void taskbar_clear_orderings()
|
||||
{
|
||||
if (!taskbar_task_orderings)
|
||||
return;
|
||||
for (GList *order = taskbar_task_orderings; order; order = order->next) {
|
||||
g_list_free_full((GList *)order->data, free);
|
||||
}
|
||||
g_list_free(taskbar_task_orderings);
|
||||
taskbar_task_orderings = NULL;
|
||||
}
|
||||
|
||||
void taskbar_save_orderings()
|
||||
{
|
||||
taskbar_clear_orderings();
|
||||
taskbar_task_orderings = NULL;
|
||||
for (int i = 0; i < num_panels; i++) {
|
||||
Panel *panel = &panels[i];
|
||||
for (int j = 0; j < panel->num_desktops; j++) {
|
||||
Taskbar *taskbar = &panel->taskbar[j];
|
||||
GList *task_order = NULL;
|
||||
for (GList *c = (taskbar->area.children && taskbarname_enabled) ? taskbar->area.children->next
|
||||
: taskbar->area.children;
|
||||
c;
|
||||
c = c->next) {
|
||||
Task *t = (Task *)c->data;
|
||||
Window *window = calloc(1, sizeof(Window));
|
||||
*window = t->win;
|
||||
task_order = g_list_append(task_order, window);
|
||||
}
|
||||
taskbar_task_orderings = g_list_append(taskbar_task_orderings, task_order);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void cleanup_taskbar()
|
||||
{
|
||||
destroy_timer(&thumbnail_update_timer_all);
|
||||
destroy_timer(&thumbnail_update_timer_active);
|
||||
destroy_timer(&thumbnail_update_timer_tooltip);
|
||||
g_list_free(taskbar_thumbnail_jobs_done);
|
||||
taskbar_save_orderings();
|
||||
if (win_to_task) {
|
||||
while (g_hash_table_size(win_to_task)) {
|
||||
GHashTableIter iter;
|
||||
@@ -116,7 +166,7 @@ void cleanup_taskbar()
|
||||
g_slist_free(urgent_list);
|
||||
urgent_list = NULL;
|
||||
|
||||
stop_timeout(urgent_timeout);
|
||||
destroy_timer(&urgent_timer);
|
||||
|
||||
for (int state = 0; state < TASK_STATE_COUNT; state++) {
|
||||
g_list_free(panel_config.g_task.gradient[state]);
|
||||
@@ -130,10 +180,18 @@ void cleanup_taskbar()
|
||||
|
||||
void init_taskbar()
|
||||
{
|
||||
INIT_TIMER(urgent_timer);
|
||||
INIT_TIMER(thumbnail_update_timer_all);
|
||||
INIT_TIMER(thumbnail_update_timer_active);
|
||||
INIT_TIMER(thumbnail_update_timer_tooltip);
|
||||
|
||||
if (!panel_config.g_task.has_text && !panel_config.g_task.has_icon) {
|
||||
panel_config.g_task.has_text = panel_config.g_task.has_icon = 1;
|
||||
}
|
||||
|
||||
if (panel_config.g_task.thumbnail_width < 8)
|
||||
panel_config.g_task.thumbnail_width = 210;
|
||||
|
||||
if (!win_to_task)
|
||||
win_to_task = g_hash_table_new_full(win_hash, win_compare, free, free_ptr_array);
|
||||
|
||||
@@ -238,9 +296,9 @@ void init_taskbar_panel(void *p)
|
||||
if ((panel->g_task.config_background_mask & (1 << TASK_URGENT)) == 0)
|
||||
panel->g_task.background[TASK_URGENT] = panel->g_task.background[TASK_ACTIVE];
|
||||
|
||||
if (!panel->g_task.maximum_width)
|
||||
if (!panel->g_task.maximum_width || !panel_horizontal)
|
||||
panel->g_task.maximum_width = server.monitors[panel->monitor].width;
|
||||
if (!panel->g_task.maximum_height)
|
||||
if (!panel->g_task.maximum_height || panel_horizontal)
|
||||
panel->g_task.maximum_height = server.monitors[panel->monitor].height;
|
||||
|
||||
if (panel_horizontal) {
|
||||
@@ -265,8 +323,9 @@ void init_taskbar_panel(void *p)
|
||||
if (!panel->g_task.background[j])
|
||||
panel->g_task.background[j] = &g_array_index(backgrounds, Background, 0);
|
||||
if (panel->g_task.background[j]->border.radius > panel->g_task.area.height / 2) {
|
||||
printf("task%sbackground_id has a too large rounded value. Please fix your tint2rc\n",
|
||||
j == 0 ? "_" : j == 1 ? "_active_" : j == 2 ? "_iconified_" : "_urgent_");
|
||||
fprintf(stderr,
|
||||
"tint2: task%sbackground_id has a too large rounded value. Please fix your tint2rc\n",
|
||||
j == 0 ? "_" : j == 1 ? "_active_" : j == 2 ? "_iconified_" : "_urgent_");
|
||||
g_array_append_val(backgrounds, *panel->g_task.background[j]);
|
||||
panel->g_task.background[j] = &g_array_index(backgrounds, Background, backgrounds->len - 1);
|
||||
panel->g_task.background[j]->border.radius = panel->g_task.area.height / 2;
|
||||
@@ -288,7 +347,8 @@ void init_taskbar_panel(void *p)
|
||||
FALSE);
|
||||
|
||||
panel->g_task.text_posx = left_bg_border_width(panel->g_task.background[0]) + panel->g_task.area.paddingxlr;
|
||||
panel->g_task.text_height = panel->g_task.area.height - (2 * panel->g_task.area.paddingy);
|
||||
panel->g_task.text_height =
|
||||
panel->g_task.area.height - (2 * panel->g_task.area.paddingy) - 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),
|
||||
MIN(panel->g_task.area.width, panel->g_task.area.height)) -
|
||||
@@ -296,16 +356,6 @@ void init_taskbar_panel(void *p)
|
||||
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.icon_posy = (panel->g_task.area.height - panel->g_task.icon_size1) / 2;
|
||||
if (0)
|
||||
printf("task: icon_size = %d, textx = %f, texth = %f, icony = %d, w = %d, h = %d, maxw = %d, maxh = %d\n",
|
||||
panel->g_task.icon_size1,
|
||||
panel->g_task.text_posx,
|
||||
panel->g_task.text_height,
|
||||
panel->g_task.icon_posy,
|
||||
panel->g_task.area.width,
|
||||
panel->g_task.area.height,
|
||||
panel->g_task.maximum_width,
|
||||
panel->g_task.maximum_height);
|
||||
}
|
||||
|
||||
Taskbar *taskbar;
|
||||
@@ -326,6 +376,22 @@ void init_taskbar_panel(void *p)
|
||||
}
|
||||
}
|
||||
init_taskbarname_panel(panel);
|
||||
taskbar_start_thumbnail_timer(THUMB_MODE_ALL);
|
||||
}
|
||||
|
||||
void taskbar_start_thumbnail_timer(ThumbnailUpdateMode mode)
|
||||
{
|
||||
if (!panel_config.g_task.thumbnail_enabled)
|
||||
return;
|
||||
if (debug_thumbnails)
|
||||
fprintf(stderr, BLUE "tint2: taskbar_start_thumbnail_timer %s" RESET "\n", mode == THUMB_MODE_ACTIVE_WINDOW ? "active" : mode == THUMB_MODE_TOOLTIP_WINDOW ? "tooltip" : "all");
|
||||
change_timer(mode == THUMB_MODE_ALL ? &thumbnail_update_timer_all :
|
||||
mode == THUMB_MODE_ACTIVE_WINDOW ? &thumbnail_update_timer_active : &thumbnail_update_timer_tooltip,
|
||||
true,
|
||||
mode == THUMB_MODE_TOOLTIP_WINDOW ? 1000 : 500,
|
||||
mode == THUMB_MODE_ALL ? 10 * 1000 : 0,
|
||||
taskbar_update_thumbnails,
|
||||
(void *)(long)mode);
|
||||
}
|
||||
|
||||
void taskbar_init_fonts()
|
||||
@@ -388,14 +454,67 @@ GPtrArray *get_task_buttons(Window win)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static Window *sort_windows = NULL;
|
||||
|
||||
int compare_windows(const void *a, const void *b)
|
||||
{
|
||||
if (!sort_windows)
|
||||
return 0;
|
||||
|
||||
int ia = *(int *)a;
|
||||
int ib = *(int *)b;
|
||||
|
||||
Window wina = sort_windows[ia];
|
||||
Window winb = sort_windows[ib];
|
||||
|
||||
for (GList *order = taskbar_task_orderings; order; order = order->next) {
|
||||
int posa = -1;
|
||||
int posb = -1;
|
||||
int pos = 0;
|
||||
for (GList *item = (GList *)order->data; item; item = item->next, pos++) {
|
||||
Window win = *(Window *)item->data;
|
||||
if (win == wina)
|
||||
posa = pos;
|
||||
if (win == winb)
|
||||
posb = pos;
|
||||
}
|
||||
if (posa >= 0 && posb >= 0) {
|
||||
return posa - posb;
|
||||
}
|
||||
}
|
||||
|
||||
return ia - ib;
|
||||
}
|
||||
|
||||
void sort_win_list(Window *windows, int count)
|
||||
{
|
||||
int *indices = (int *)calloc(count, sizeof(int));
|
||||
for (int i = 0; i < count; i++)
|
||||
indices[i] = i;
|
||||
sort_windows = windows;
|
||||
qsort(indices, count, sizeof(int), compare_windows);
|
||||
Window *result = (Window *)calloc(count, sizeof(Window));
|
||||
for (int i = 0; i < count; i++)
|
||||
result[i] = windows[indices[i]];
|
||||
memcpy(windows, result, count * sizeof(Window));
|
||||
free(result);
|
||||
free(indices);
|
||||
sort_windows = NULL;
|
||||
}
|
||||
|
||||
void taskbar_refresh_tasklist()
|
||||
{
|
||||
if (!taskbar_enabled)
|
||||
return;
|
||||
// fprintf(stderr, "%s %d:\n", __FUNCTION__, __LINE__);
|
||||
|
||||
int num_results;
|
||||
Window *win = server_get_property(server.root_win, server.atom._NET_CLIENT_LIST, XA_WINDOW, &num_results);
|
||||
Window *sorted = (Window *)calloc(num_results, sizeof(Window));
|
||||
memcpy(sorted, win, num_results * sizeof(Window));
|
||||
if (taskbar_task_orderings) {
|
||||
sort_win_list(sorted, num_results);
|
||||
taskbar_clear_orderings();
|
||||
}
|
||||
if (!win)
|
||||
return;
|
||||
|
||||
@@ -403,7 +522,7 @@ void taskbar_refresh_tasklist()
|
||||
for (GList *it = win_list; it; it = it->next) {
|
||||
int i;
|
||||
for (i = 0; i < num_results; i++)
|
||||
if (*((Window *)it->data) == win[i])
|
||||
if (*((Window *)it->data) == sorted[i])
|
||||
break;
|
||||
if (i == num_results)
|
||||
taskbar_remove_task(it->data);
|
||||
@@ -412,10 +531,11 @@ void taskbar_refresh_tasklist()
|
||||
|
||||
// Add any new
|
||||
for (int i = 0; i < num_results; i++)
|
||||
if (!get_task(win[i]))
|
||||
add_task(win[i]);
|
||||
if (!get_task(sorted[i]))
|
||||
add_task(sorted[i]);
|
||||
|
||||
XFree(win);
|
||||
free(sorted);
|
||||
}
|
||||
|
||||
int taskbar_compute_desired_size(void *obj)
|
||||
@@ -439,7 +559,6 @@ gboolean resize_taskbar(void *obj)
|
||||
Taskbar *taskbar = (Taskbar *)obj;
|
||||
Panel *panel = (Panel *)taskbar->area.panel;
|
||||
|
||||
// printf("resize_taskbar %d %d\n", taskbar->area.posx, taskbar->area.posy);
|
||||
if (panel_horizontal) {
|
||||
relayout_with_constraint(&taskbar->area, panel->g_task.maximum_width);
|
||||
|
||||
@@ -534,6 +653,15 @@ void set_taskbar_state(Taskbar *taskbar, TaskbarState state)
|
||||
for (; l; l = l->next)
|
||||
schedule_redraw((Area *)l->data);
|
||||
}
|
||||
if (taskbar_mode == MULTI_DESKTOP && hide_task_diff_desktop) {
|
||||
GList *l = taskbar->area.children;
|
||||
if (taskbarname_enabled)
|
||||
l = l->next;
|
||||
for (; l; l = l->next) {
|
||||
Task *task = (Task *)l->data;
|
||||
set_task_state(task, task->current_state);
|
||||
}
|
||||
}
|
||||
}
|
||||
schedule_panel_redraw();
|
||||
}
|
||||
@@ -688,3 +816,48 @@ void update_minimized_icon_positions(void *p)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void taskbar_update_thumbnails(void *arg)
|
||||
{
|
||||
if (!panel_config.g_task.thumbnail_enabled)
|
||||
return;
|
||||
ThumbnailUpdateMode mode = (ThumbnailUpdateMode)(long)arg;
|
||||
if (debug_thumbnails)
|
||||
fprintf(stderr, BLUE "tint2: taskbar_update_thumbnails %s" RESET "\n", mode == THUMB_MODE_ACTIVE_WINDOW ? "active" : mode == THUMB_MODE_TOOLTIP_WINDOW ? "tooltip" : "all");
|
||||
double start_time = get_time();
|
||||
for (int i = 0; i < num_panels; i++) {
|
||||
Panel *panel = &panels[i];
|
||||
for (int j = 0; j < panel->num_desktops; j++) {
|
||||
Taskbar *taskbar = &panel->taskbar[j];
|
||||
for (GList *c = (taskbar->area.children && taskbarname_enabled) ? taskbar->area.children->next
|
||||
: taskbar->area.children;
|
||||
c;
|
||||
c = c->next) {
|
||||
Task *t = (Task *)c->data;
|
||||
if ((mode == THUMB_MODE_ALL && t->current_state == TASK_ACTIVE && !g_list_find(taskbar_thumbnail_jobs_done, t)) || (mode == THUMB_MODE_ACTIVE_WINDOW && t->current_state == TASK_ACTIVE) ||
|
||||
(mode == THUMB_MODE_TOOLTIP_WINDOW && g_tooltip.mapped && g_tooltip.area == &t->area)) {
|
||||
task_refresh_thumbnail(t);
|
||||
if (mode == THUMB_MODE_ALL)
|
||||
taskbar_thumbnail_jobs_done = g_list_append(taskbar_thumbnail_jobs_done, t);
|
||||
if (t->thumbnail && mode == THUMB_MODE_TOOLTIP_WINDOW) {
|
||||
taskbar_start_thumbnail_timer(THUMB_MODE_TOOLTIP_WINDOW);
|
||||
}
|
||||
}
|
||||
if (mode == THUMB_MODE_ALL) {
|
||||
double now = get_time();
|
||||
if (now - start_time > 0.030) {
|
||||
change_timer(&thumbnail_update_timer_all, true, 50, 10 * 1000, taskbar_update_thumbnails, arg);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (mode == THUMB_MODE_ALL) {
|
||||
if (taskbar_thumbnail_jobs_done) {
|
||||
g_list_free(taskbar_thumbnail_jobs_done);
|
||||
taskbar_thumbnail_jobs_done = NULL;
|
||||
change_timer(&thumbnail_update_timer_all, true, 10 * 1000, 10 * 1000, taskbar_update_thumbnails, arg);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,6 +25,12 @@ typedef enum TaskbarSortMethod {
|
||||
TASKBAR_SORT_MRU,
|
||||
} TaskbarSortMethod;
|
||||
|
||||
typedef enum ThumbnailUpdateMode {
|
||||
THUMB_MODE_ACTIVE_WINDOW = 0,
|
||||
THUMB_MODE_TOOLTIP_WINDOW,
|
||||
THUMB_MODE_ALL
|
||||
} ThumbnailUpdateMode;
|
||||
|
||||
typedef struct {
|
||||
Area area;
|
||||
gchar *name;
|
||||
@@ -49,6 +55,7 @@ typedef struct GlobalTaskbar {
|
||||
|
||||
extern gboolean taskbar_enabled;
|
||||
extern gboolean taskbar_distribute_size;
|
||||
extern gboolean hide_task_diff_desktop;
|
||||
extern gboolean hide_inactive_tasks;
|
||||
extern gboolean hide_task_diff_monitor;
|
||||
extern gboolean hide_taskbar_if_empty;
|
||||
@@ -71,6 +78,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();
|
||||
|
||||
@@ -214,3 +214,35 @@ void draw_taskbarname(void *obj, cairo_t *c)
|
||||
|
||||
g_object_unref(layout);
|
||||
}
|
||||
|
||||
void update_desktop_names()
|
||||
{
|
||||
if (!taskbarname_enabled)
|
||||
return;
|
||||
GSList *list = get_desktop_names();
|
||||
for (int i = 0; i < num_panels; i++) {
|
||||
int j;
|
||||
GSList *l;
|
||||
for (j = 0, l = list; j < panels[i].num_desktops; j++) {
|
||||
gchar *name;
|
||||
if (l) {
|
||||
name = g_strdup(l->data);
|
||||
l = l->next;
|
||||
} else {
|
||||
name = g_strdup_printf("%d", j + 1);
|
||||
}
|
||||
Taskbar *taskbar = &panels[i].taskbar[j];
|
||||
if (strcmp(name, taskbar->bar_name.name) != 0) {
|
||||
g_free(taskbar->bar_name.name);
|
||||
taskbar->bar_name.name = name;
|
||||
taskbar->bar_name.area.resize_needed = 1;
|
||||
} else {
|
||||
g_free(name);
|
||||
}
|
||||
}
|
||||
}
|
||||
for (GSList *l = list; l; l = l->next)
|
||||
g_free(l->data);
|
||||
g_slist_free(list);
|
||||
schedule_panel_redraw();
|
||||
}
|
||||
|
||||
@@ -23,4 +23,6 @@ gboolean resize_taskbarname(void *obj);
|
||||
|
||||
void taskbarname_default_font_changed();
|
||||
|
||||
void update_desktop_names();
|
||||
|
||||
#endif
|
||||
|
||||
2185
src/tint.c
2185
src/tint.c
File diff suppressed because it is too large
Load Diff
@@ -22,8 +22,13 @@ include_directories( ../util
|
||||
set(SOURCES ../util/common.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
|
||||
|
||||
@@ -6,7 +6,7 @@ GtkWidget *current_background, *background_fill_color, *background_border_color,
|
||||
*background_fill_color_over, *background_border_color_over, *background_gradient_over, *background_fill_color_press,
|
||||
*background_border_color_press, *background_gradient_press, *background_border_width, *background_corner_radius,
|
||||
*background_border_sides_top, *background_border_sides_bottom, *background_border_sides_left,
|
||||
*background_border_sides_right;
|
||||
*background_border_sides_right, *background_border_content_tint_weight, *background_fill_content_tint_weight;
|
||||
|
||||
GtkWidget *create_background_combo(const char *label)
|
||||
{
|
||||
@@ -115,7 +115,9 @@ void create_background(GtkWidget *parent)
|
||||
GTK_TYPE_BOOL,
|
||||
GTK_TYPE_BOOL,
|
||||
GTK_TYPE_BOOL,
|
||||
GTK_TYPE_BOOL);
|
||||
GTK_TYPE_BOOL,
|
||||
GTK_TYPE_DOUBLE,
|
||||
GTK_TYPE_DOUBLE);
|
||||
|
||||
GtkWidget *table, *label, *button;
|
||||
int row, col;
|
||||
@@ -175,6 +177,19 @@ void create_background(GtkWidget *parent)
|
||||
col++;
|
||||
gtk_tooltips_set_tip(tooltips, background_fill_color, _("The fill color of the current background"), NULL);
|
||||
|
||||
row++, col = 2;
|
||||
label = gtk_label_new(_("Fill tint"));
|
||||
gtk_misc_set_alignment(GTK_MISC(label), 0, 0);
|
||||
gtk_widget_show(label);
|
||||
gtk_table_attach(GTK_TABLE(table), label, col, col + 1, row, row + 1, GTK_FILL, 0, 0, 0);
|
||||
col++;
|
||||
|
||||
background_fill_content_tint_weight = gtk_spin_button_new_with_range(0, 100, 1);
|
||||
gtk_widget_show(background_fill_content_tint_weight);
|
||||
gtk_table_attach(GTK_TABLE(table), background_fill_content_tint_weight, col, col + 1, row, row + 1, GTK_FILL, 0, 0, 0);
|
||||
col++;
|
||||
gtk_tooltips_set_tip(tooltips, background_fill_content_tint_weight, _("How much the border color should be tinted with the content color"), NULL);
|
||||
|
||||
row++, col = 2;
|
||||
label = gtk_label_new(_("Border color"));
|
||||
gtk_misc_set_alignment(GTK_MISC(label), 0, 0);
|
||||
@@ -189,6 +204,19 @@ void create_background(GtkWidget *parent)
|
||||
col++;
|
||||
gtk_tooltips_set_tip(tooltips, background_border_color, _("The border color of the current background"), NULL);
|
||||
|
||||
row++, col = 2;
|
||||
label = gtk_label_new(_("Border tint"));
|
||||
gtk_misc_set_alignment(GTK_MISC(label), 0, 0);
|
||||
gtk_widget_show(label);
|
||||
gtk_table_attach(GTK_TABLE(table), label, col, col + 1, row, row + 1, GTK_FILL, 0, 0, 0);
|
||||
col++;
|
||||
|
||||
background_border_content_tint_weight = gtk_spin_button_new_with_range(0, 100, 1);
|
||||
gtk_widget_show(background_border_content_tint_weight);
|
||||
gtk_table_attach(GTK_TABLE(table), background_border_content_tint_weight, col, col + 1, row, row + 1, GTK_FILL, 0, 0, 0);
|
||||
col++;
|
||||
gtk_tooltips_set_tip(tooltips, background_border_content_tint_weight, _("How much the border color should be tinted with the content color"), NULL);
|
||||
|
||||
row++, col = 2;
|
||||
label = gtk_label_new(_("Gradient"));
|
||||
gtk_misc_set_alignment(GTK_MISC(label), 0, 0);
|
||||
@@ -366,6 +394,8 @@ void create_background(GtkWidget *parent)
|
||||
g_signal_connect(G_OBJECT(background_border_sides_bottom), "toggled", G_CALLBACK(background_update), NULL);
|
||||
g_signal_connect(G_OBJECT(background_border_sides_left), "toggled", G_CALLBACK(background_update), NULL);
|
||||
g_signal_connect(G_OBJECT(background_border_sides_right), "toggled", G_CALLBACK(background_update), NULL);
|
||||
g_signal_connect(G_OBJECT(background_border_content_tint_weight), "value-changed", G_CALLBACK(background_update), NULL);
|
||||
g_signal_connect(G_OBJECT(background_fill_content_tint_weight), "value-changed", G_CALLBACK(background_update), NULL);
|
||||
|
||||
change_paragraph(parent);
|
||||
}
|
||||
@@ -750,6 +780,9 @@ void background_update(GtkWidget *widget, gpointer data)
|
||||
r = gtk_spin_button_get_value(GTK_SPIN_BUTTON(background_corner_radius));
|
||||
b = gtk_spin_button_get_value(GTK_SPIN_BUTTON(background_border_width));
|
||||
|
||||
double fill_weight = gtk_spin_button_get_value(GTK_SPIN_BUTTON(background_fill_content_tint_weight));
|
||||
double border_weight = gtk_spin_button_get_value(GTK_SPIN_BUTTON(background_border_content_tint_weight));
|
||||
|
||||
gboolean sideTop = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(background_border_sides_top));
|
||||
gboolean sideBottom = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(background_border_sides_bottom));
|
||||
gboolean sideLeft = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(background_border_sides_left));
|
||||
@@ -836,6 +869,10 @@ void background_update(GtkWidget *widget, gpointer data)
|
||||
sideLeft,
|
||||
bgColBorderSidesRight,
|
||||
sideRight,
|
||||
bgColFillWeight,
|
||||
fill_weight,
|
||||
bgColBorderWeight,
|
||||
border_weight,
|
||||
-1);
|
||||
background_update_image(index);
|
||||
}
|
||||
@@ -862,6 +899,8 @@ void current_background_changed(GtkWidget *widget, gpointer data)
|
||||
gtk_widget_set_sensitive(background_border_sides_left, index > 0);
|
||||
gtk_widget_set_sensitive(background_border_sides_right, index > 0);
|
||||
gtk_widget_set_sensitive(background_corner_radius, index > 0);
|
||||
gtk_widget_set_sensitive(background_border_content_tint_weight, index > 0);
|
||||
gtk_widget_set_sensitive(background_fill_content_tint_weight, index > 0);
|
||||
|
||||
background_updates_disabled = TRUE;
|
||||
|
||||
@@ -875,6 +914,9 @@ void current_background_changed(GtkWidget *widget, gpointer data)
|
||||
int r;
|
||||
int b;
|
||||
|
||||
double fill_weight;
|
||||
double border_weight;
|
||||
|
||||
gboolean sideTop;
|
||||
gboolean sideBottom;
|
||||
gboolean sideLeft;
|
||||
@@ -938,6 +980,10 @@ void current_background_changed(GtkWidget *widget, gpointer data)
|
||||
&sideLeft,
|
||||
bgColBorderSidesRight,
|
||||
&sideRight,
|
||||
bgColFillWeight,
|
||||
&fill_weight,
|
||||
bgColBorderWeight,
|
||||
&border_weight,
|
||||
-1);
|
||||
|
||||
gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(background_border_sides_top), sideTop);
|
||||
@@ -968,6 +1014,9 @@ void current_background_changed(GtkWidget *widget, gpointer data)
|
||||
gtk_spin_button_set_value(GTK_SPIN_BUTTON(background_border_width), b);
|
||||
gtk_spin_button_set_value(GTK_SPIN_BUTTON(background_corner_radius), r);
|
||||
|
||||
gtk_spin_button_set_value(GTK_SPIN_BUTTON(background_fill_content_tint_weight), fill_weight);
|
||||
gtk_spin_button_set_value(GTK_SPIN_BUTTON(background_border_content_tint_weight), border_weight);
|
||||
|
||||
g_boxed_free(GDK_TYPE_COLOR, fillColor);
|
||||
g_boxed_free(GDK_TYPE_COLOR, borderColor);
|
||||
g_boxed_free(GDK_TYPE_COLOR, fillColorOver);
|
||||
|
||||
@@ -100,7 +100,7 @@ char *file_name_from_path(const char *filepath)
|
||||
|
||||
void make_backup(const char *filepath)
|
||||
{
|
||||
gchar *backup_path = g_strdup_printf("%s.backup.%ld", filepath, time(NULL));
|
||||
gchar *backup_path = g_strdup_printf("%s.backup.%lld", filepath, (long long)time(NULL));
|
||||
copy_file(filepath, backup_path);
|
||||
g_free(backup_path);
|
||||
}
|
||||
@@ -227,7 +227,7 @@ int main(int argc, char **argv)
|
||||
{
|
||||
gchar *tint2_config_dir = g_build_filename(g_get_user_config_dir(), "tint2", NULL);
|
||||
if (!g_file_test(tint2_config_dir, G_FILE_TEST_IS_DIR))
|
||||
g_mkdir(tint2_config_dir, 0700);
|
||||
g_mkdir_with_parents(tint2_config_dir, 0700);
|
||||
g_free(tint2_config_dir);
|
||||
}
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
2418
src/tint2conf/po/es.po
Normal file
2418
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
36
src/tint2conf/po/update-translations.sh
Executable file
36
src/tint2conf/po/update-translations.sh
Executable file
@@ -0,0 +1,36 @@
|
||||
#!/bin/bash
|
||||
|
||||
set -e
|
||||
set -x
|
||||
|
||||
find .. -name '*.c' | sort -r | xargs xgettext --keyword=_ --language=C --output=tint2conf.pot -
|
||||
sed -i "s/PACKAGE VERSION/tint2conf $(../../../get_version.sh | head -n1)/g" tint2conf.pot
|
||||
sed -i "s/CHARSET/UTF-8/g" tint2conf.pot
|
||||
|
||||
for f in *.po
|
||||
do
|
||||
lang=$(basename $f .po)
|
||||
echo $lang
|
||||
msgmerge -i -o $lang.pox $lang.po tint2conf.pot
|
||||
cat ${lang}.pox > ${lang}.po
|
||||
rm ${lang}.pox
|
||||
done
|
||||
|
||||
set +e
|
||||
set +x
|
||||
echo "Status:"
|
||||
for f in *.po
|
||||
do
|
||||
lang=$(basename $f .po)
|
||||
fuzzy=$(cat ${lang}.po | grep -A2 "#, fuzzy")
|
||||
missing=$(cat ${lang}.po | grep -B1 'msgstr ""')
|
||||
if [ -z "$fuzzy" ] && [ -z "$missing" ]
|
||||
then
|
||||
echo $lang ": Up to date"
|
||||
else
|
||||
count=$(( $(echo -e "$fuzzy" "\n" "$missing" | grep "^--$" | wc -l) + 1))
|
||||
echo "${lang}: Translation incomplete: ${count} strings to be updated. See ${lang}.todo"
|
||||
echo "$fuzzy" > ${lang}.todo
|
||||
echo "$missing" >> ${lang}.todo
|
||||
fi
|
||||
done
|
||||
@@ -20,6 +20,7 @@
|
||||
#include "gui.h"
|
||||
#include "background_gui.h"
|
||||
#include "gradient_gui.h"
|
||||
#include "strlcat.h"
|
||||
|
||||
GtkWidget *panel_width, *panel_height, *panel_margin_x, *panel_margin_y, *panel_padding_x, *panel_padding_y,
|
||||
*panel_spacing;
|
||||
@@ -31,7 +32,7 @@ GtkWidget *panel_window_name, *disable_transparency;
|
||||
GtkWidget *panel_mouse_effects;
|
||||
GtkWidget *mouse_hover_icon_opacity, *mouse_hover_icon_saturation, *mouse_hover_icon_brightness;
|
||||
GtkWidget *mouse_pressed_icon_opacity, *mouse_pressed_icon_saturation, *mouse_pressed_icon_brightness;
|
||||
GtkWidget *panel_primary_monitor_first, *panel_shrink;
|
||||
GtkWidget *panel_shrink;
|
||||
|
||||
GtkListStore *panel_items, *all_items;
|
||||
GtkWidget *panel_items_view, *all_items_view;
|
||||
@@ -44,7 +45,7 @@ GtkWidget *notebook;
|
||||
|
||||
// taskbar
|
||||
GtkWidget *taskbar_show_desktop, *taskbar_show_name, *taskbar_padding_x, *taskbar_padding_y, *taskbar_spacing;
|
||||
GtkWidget *taskbar_hide_inactive_tasks, *taskbar_hide_diff_monitor;
|
||||
GtkWidget *taskbar_hide_inactive_tasks, *taskbar_hide_diff_monitor, *taskbar_hide_diff_desktop;
|
||||
GtkWidget *taskbar_name_padding_x, *taskbar_name_padding_y, *taskbar_name_inactive_color, *taskbar_name_active_color;
|
||||
GtkWidget *taskbar_name_font, *taskbar_name_font_set;
|
||||
GtkWidget *taskbar_active_background, *taskbar_inactive_background;
|
||||
@@ -80,10 +81,10 @@ GtkWidget *clock_font_line1, *clock_font_line1_set, *clock_font_line2, *clock_fo
|
||||
GtkWidget *clock_background;
|
||||
|
||||
// battery
|
||||
GtkWidget *battery_hide_if_higher, *battery_alert_if_lower, *battery_alert_cmd;
|
||||
GtkWidget *battery_hide_if_higher, *battery_alert_if_lower, *battery_alert_cmd, *battery_alert_full_cmd;
|
||||
GtkWidget *battery_padding_x, *battery_padding_y;
|
||||
GtkWidget *battery_font_line1, *battery_font_line1_set, *battery_font_line2, *battery_font_line2_set,
|
||||
*battery_font_color;
|
||||
*battery_font_color, *battery_format1, *battery_format2;
|
||||
GtkWidget *battery_background;
|
||||
GtkWidget *battery_tooltip;
|
||||
GtkWidget *battery_left_command, *battery_mclick_command, *battery_right_command, *battery_uwheel_command,
|
||||
@@ -97,7 +98,7 @@ GtkWidget *systray_background, *systray_monitor, *systray_name_filter;
|
||||
|
||||
// tooltip
|
||||
GtkWidget *tooltip_padding_x, *tooltip_padding_y, *tooltip_font, *tooltip_font_set, *tooltip_font_color;
|
||||
GtkWidget *tooltip_task_show, *tooltip_show_after, *tooltip_hide_after;
|
||||
GtkWidget *tooltip_task_show, *tooltip_show_after, *tooltip_hide_after, *tooltip_task_thumbnail, *tooltip_task_thumbnail_size;
|
||||
GtkWidget *clock_format_tooltip, *clock_tmz_tooltip;
|
||||
GtkWidget *tooltip_background;
|
||||
|
||||
@@ -180,7 +181,7 @@ void applyClicked(GtkWidget *widget, gpointer data)
|
||||
gchar *filepath = get_current_theme_path();
|
||||
if (filepath) {
|
||||
if (config_is_manual(filepath)) {
|
||||
gchar *backup_path = g_strdup_printf("%s.backup.%ld", filepath, time(NULL));
|
||||
gchar *backup_path = g_strdup_printf("%s.backup.%lld", filepath, (long long)time(NULL));
|
||||
copy_file(filepath, backup_path);
|
||||
g_free(backup_path);
|
||||
}
|
||||
@@ -477,6 +478,7 @@ void create_panel(GtkWidget *parent)
|
||||
gtk_table_attach(GTK_TABLE(table), panel_combo_monitor, col, col + 1, row, row + 1, GTK_FILL, 0, 0, 0);
|
||||
col++;
|
||||
gtk_combo_box_append_text(GTK_COMBO_BOX(panel_combo_monitor), _("All"));
|
||||
gtk_combo_box_append_text(GTK_COMBO_BOX(panel_combo_monitor), _("Primary"));
|
||||
gtk_combo_box_append_text(GTK_COMBO_BOX(panel_combo_monitor), _("1"));
|
||||
gtk_combo_box_append_text(GTK_COMBO_BOX(panel_combo_monitor), _("2"));
|
||||
gtk_combo_box_append_text(GTK_COMBO_BOX(panel_combo_monitor), _("3"));
|
||||
@@ -486,24 +488,6 @@ void create_panel(GtkWidget *parent)
|
||||
gtk_combo_box_set_active(GTK_COMBO_BOX(panel_combo_monitor), 0);
|
||||
gtk_tooltips_set_tip(tooltips, panel_combo_monitor, _("The monitor on which the panel is placed"), NULL);
|
||||
|
||||
row++;
|
||||
col = 2;
|
||||
label = gtk_label_new(_("Primary monitor first"));
|
||||
gtk_misc_set_alignment(GTK_MISC(label), 0, 0);
|
||||
gtk_widget_show(label);
|
||||
gtk_table_attach(GTK_TABLE(table), label, col, col + 1, row, row + 1, GTK_FILL, 0, 0, 0);
|
||||
col++;
|
||||
|
||||
panel_primary_monitor_first = gtk_check_button_new();
|
||||
gtk_widget_show(panel_primary_monitor_first);
|
||||
gtk_table_attach(GTK_TABLE(table), panel_primary_monitor_first, col, col + 1, row, row + 1, GTK_FILL, 0, 0, 0);
|
||||
col++;
|
||||
gtk_tooltips_set_tip(tooltips,
|
||||
panel_primary_monitor_first,
|
||||
_("If enabled, the primary monitor will have index 1 in the monitor list even if it is not "
|
||||
"top-left."),
|
||||
NULL);
|
||||
|
||||
row++;
|
||||
col = 2;
|
||||
label = gtk_label_new(_("Length"));
|
||||
@@ -1273,7 +1257,8 @@ gboolean panel_contains(const char *value)
|
||||
|
||||
char *get_panel_items()
|
||||
{
|
||||
char *result = calloc(1, 256 * sizeof(char));
|
||||
size_t buf_size = 256;
|
||||
char *result = calloc(buf_size, 1);
|
||||
GtkTreeModel *model = GTK_TREE_MODEL(panel_items);
|
||||
|
||||
GtkTreeIter i;
|
||||
@@ -1284,7 +1269,7 @@ char *get_panel_items()
|
||||
while (1) {
|
||||
gchar *v;
|
||||
gtk_tree_model_get(model, &i, itemsColValue, &v, -1);
|
||||
strcat(result, v);
|
||||
strlcat(result, v, buf_size);
|
||||
|
||||
if (!gtk_tree_model_iter_next(model, &i)) {
|
||||
break;
|
||||
@@ -1329,19 +1314,19 @@ void set_panel_items(const char *items)
|
||||
} else if (v == ':') {
|
||||
separator_index++;
|
||||
buffer[0] = 0;
|
||||
sprintf(buffer, "%s %d", _("Separator"), separator_index + 1);
|
||||
snprintf(buffer, sizeof(buffer), "%s %d", _("Separator"), separator_index + 1);
|
||||
name = buffer;
|
||||
value = ":";
|
||||
} else if (v == 'E') {
|
||||
execp_index++;
|
||||
buffer[0] = 0;
|
||||
sprintf(buffer, "%s %d", _("Executor"), execp_index + 1);
|
||||
snprintf(buffer, sizeof(buffer), "%s %d", _("Executor"), execp_index + 1);
|
||||
name = buffer;
|
||||
value = "E";
|
||||
} else if (v == 'P') {
|
||||
button_index++;
|
||||
buffer[0] = 0;
|
||||
sprintf(buffer, "%s %d", _("Button"), button_index + 1);
|
||||
snprintf(buffer, sizeof(buffer), "%s %d", _("Button"), button_index + 1);
|
||||
name = buffer;
|
||||
value = "P";
|
||||
} else {
|
||||
@@ -1856,7 +1841,7 @@ void load_desktop_file(const char *file, gboolean selected)
|
||||
if (pixbuf)
|
||||
g_object_unref(pixbuf);
|
||||
} else {
|
||||
printf("Could not load %s\n", file);
|
||||
fprintf(stderr, "tint2: Could not load %s\n", file);
|
||||
GdkPixbuf *pixbuf = load_icon(DEFAULT_ICON);
|
||||
GtkTreeIter iter;
|
||||
gtk_list_store_append(store, &iter);
|
||||
@@ -1914,7 +1899,7 @@ void load_desktop_entry(const char *file, GList **entries)
|
||||
|
||||
DesktopEntry *entry = calloc(1, sizeof(DesktopEntry));
|
||||
if (!read_desktop_file(file, entry))
|
||||
printf("Could not load %s\n", file);
|
||||
fprintf(stderr, "tint2: Could not load %s\n", file);
|
||||
if (entry->hidden_from_menus) {
|
||||
free(entry);
|
||||
return;
|
||||
@@ -2497,7 +2482,7 @@ void create_launcher(GtkWidget *parent, GtkWindow *window)
|
||||
|
||||
change_paragraph(parent);
|
||||
|
||||
fprintf(stderr, "Loading icon themes\n");
|
||||
fprintf(stderr, "tint2: Loading icon themes\n");
|
||||
GList *themes = NULL;
|
||||
const GSList *location;
|
||||
for (location = get_icon_locations(); location; location = g_slist_next(location)) {
|
||||
@@ -2526,9 +2511,9 @@ void create_launcher(GtkWidget *parent, GtkWindow *window)
|
||||
free_icon_theme((IconTheme *)l->data);
|
||||
}
|
||||
g_list_free(themes);
|
||||
fprintf(stderr, "Icon themes loaded\n");
|
||||
fprintf(stderr, "tint2: Icon themes loaded\n");
|
||||
|
||||
fprintf(stderr, "Loading .desktop files\n");
|
||||
fprintf(stderr, "tint2: Loading .desktop files\n");
|
||||
GList *entries = NULL;
|
||||
for (location = get_apps_locations(); location; location = g_slist_next(location)) {
|
||||
const gchar *path = (gchar *)location->data;
|
||||
@@ -2545,7 +2530,7 @@ void create_launcher(GtkWidget *parent, GtkWindow *window)
|
||||
icon_theme_changed(window);
|
||||
load_icons(launcher_apps);
|
||||
load_icons(all_apps);
|
||||
fprintf(stderr, "Desktop files loaded\n");
|
||||
fprintf(stderr, "tint2: Desktop files loaded\n");
|
||||
}
|
||||
|
||||
void create_taskbar(GtkWidget *parent)
|
||||
@@ -2651,6 +2636,19 @@ void create_taskbar(GtkWidget *parent)
|
||||
"This behavior is enabled automatically if the panel monitor is set to 'All'."),
|
||||
NULL);
|
||||
|
||||
col = 2;
|
||||
row++;
|
||||
label = gtk_label_new(_("Hide tasks from different desktops"));
|
||||
gtk_misc_set_alignment(GTK_MISC(label), 0, 0);
|
||||
gtk_widget_show(label);
|
||||
gtk_table_attach(GTK_TABLE(table), label, col, col + 1, row, row + 1, GTK_FILL, 0, 0, 0);
|
||||
col++;
|
||||
|
||||
taskbar_hide_diff_desktop = gtk_check_button_new();
|
||||
gtk_widget_show(taskbar_hide_diff_desktop);
|
||||
gtk_table_attach(GTK_TABLE(table), taskbar_hide_diff_desktop, col, col + 1, row, row + 1, GTK_FILL, 0, 0, 0);
|
||||
col++;
|
||||
|
||||
col = 2;
|
||||
row++;
|
||||
label = gtk_label_new(_("Always show all desktop tasks"));
|
||||
@@ -3292,6 +3290,36 @@ void create_task(GtkWidget *parent)
|
||||
"over task buttons."),
|
||||
NULL);
|
||||
|
||||
row++, col = 2;
|
||||
label = gtk_label_new(_("Thumbnails"));
|
||||
gtk_misc_set_alignment(GTK_MISC(label), 0, 0);
|
||||
gtk_widget_show(label);
|
||||
gtk_table_attach(GTK_TABLE(table), label, col, col + 1, row, row + 1, GTK_FILL, 0, 0, 0);
|
||||
col++;
|
||||
|
||||
tooltip_task_thumbnail = gtk_check_button_new();
|
||||
gtk_widget_show(tooltip_task_thumbnail);
|
||||
gtk_table_attach(GTK_TABLE(table), tooltip_task_thumbnail, col, col + 1, row, row + 1, GTK_FILL, 0, 0, 0);
|
||||
col++;
|
||||
gtk_tooltips_set_tip(tooltips,
|
||||
tooltip_task_thumbnail,
|
||||
_("If enabled, a tooltip showing the window thumbnail is displayed when the mouse cursor moves "
|
||||
"over task buttons."),
|
||||
NULL);
|
||||
|
||||
row++, col = 2;
|
||||
label = gtk_label_new(_("Thumbnail size"));
|
||||
gtk_misc_set_alignment(GTK_MISC(label), 0, 0);
|
||||
gtk_widget_show(label);
|
||||
gtk_table_attach(GTK_TABLE(table), label, col, col + 1, row, row + 1, GTK_FILL, 0, 0, 0);
|
||||
col++;
|
||||
|
||||
tooltip_task_thumbnail_size = gtk_spin_button_new_with_range(8, 9000, 1);
|
||||
gtk_spin_button_set_value(GTK_SPIN_BUTTON(tooltip_task_thumbnail_size), 210);
|
||||
gtk_widget_show(tooltip_task_thumbnail_size);
|
||||
gtk_table_attach(GTK_TABLE(table), tooltip_task_thumbnail_size, col, col + 1, row, row + 1, GTK_FILL, 0, 0, 0);
|
||||
col++;
|
||||
|
||||
row++, col = 2;
|
||||
label = gtk_label_new(_("Maximum width"));
|
||||
gtk_misc_set_alignment(GTK_MISC(label), 0, 0);
|
||||
@@ -4071,7 +4099,7 @@ void create_separator(GtkWidget *notebook, int i)
|
||||
Separator *separator = &g_array_index(separators, Separator, i);
|
||||
|
||||
separator->name[0] = 0;
|
||||
sprintf(separator->name, "%s %d", _("Separator"), i + 1);
|
||||
snprintf(separator->name, sizeof(separator->name), "%s %d", _("Separator"), i + 1);
|
||||
separator->page_label = gtk_label_new(separator->name);
|
||||
gtk_widget_show(separator->page_label);
|
||||
separator->page_separator = gtk_vbox_new(FALSE, DEFAULT_HOR_SPACING);
|
||||
@@ -4197,7 +4225,7 @@ void create_execp(GtkWidget *notebook, int i)
|
||||
Executor *executor = &g_array_index(executors, Executor, i);
|
||||
|
||||
executor->name[0] = 0;
|
||||
sprintf(executor->name, "%s %d", _("Executor"), i + 1);
|
||||
snprintf(executor->name, sizeof(executor->name), "%s %d", _("Executor"), i + 1);
|
||||
executor->page_label = gtk_label_new(executor->name);
|
||||
gtk_widget_show(executor->page_label);
|
||||
executor->page_execp = gtk_vbox_new(FALSE, DEFAULT_HOR_SPACING);
|
||||
@@ -4618,7 +4646,7 @@ void create_button(GtkWidget *notebook, int i)
|
||||
Button *button = &g_array_index(buttons, Button, i);
|
||||
|
||||
button->name[0] = 0;
|
||||
sprintf(button->name, "%s %d", _("Button"), i + 1);
|
||||
snprintf(button->name, sizeof(button->name), "%s %d", _("Button"), i + 1);
|
||||
button->page_label = gtk_label_new(button->name);
|
||||
gtk_widget_show(button->page_label);
|
||||
button->page_button = gtk_vbox_new(FALSE, DEFAULT_HOR_SPACING);
|
||||
@@ -5007,7 +5035,7 @@ void separator_update_indices()
|
||||
{
|
||||
for (int i = 0; i < separators->len; i++) {
|
||||
Separator *separator = &g_array_index(separators, Separator, i);
|
||||
sprintf(separator->name, "%s %d", _("Separator"), i + 1);
|
||||
snprintf(separator->name, sizeof(separator->name), "%s %d", _("Separator"), i + 1);
|
||||
gtk_label_set_text(GTK_LABEL(separator->page_label), separator->name);
|
||||
}
|
||||
|
||||
@@ -5025,7 +5053,7 @@ void separator_update_indices()
|
||||
separator_index++;
|
||||
char buffer[256];
|
||||
buffer[0] = 0;
|
||||
sprintf(buffer, "%s %d", _("Separator"), separator_index + 1);
|
||||
snprintf(buffer, sizeof(buffer), "%s %d", _("Separator"), separator_index + 1);
|
||||
|
||||
gtk_list_store_set(panel_items, &iter, itemsColName, buffer, -1);
|
||||
}
|
||||
@@ -5039,7 +5067,7 @@ void execp_update_indices()
|
||||
{
|
||||
for (int i = 0; i < executors->len; i++) {
|
||||
Executor *executor = &g_array_index(executors, Executor, i);
|
||||
sprintf(executor->name, "%s %d", _("Executor"), i + 1);
|
||||
snprintf(executor->name, sizeof(executor->name), "%s %d", _("Executor"), i + 1);
|
||||
gtk_label_set_text(GTK_LABEL(executor->page_label), executor->name);
|
||||
}
|
||||
|
||||
@@ -5057,7 +5085,7 @@ void execp_update_indices()
|
||||
execp_index++;
|
||||
char buffer[256];
|
||||
buffer[0] = 0;
|
||||
sprintf(buffer, "%s %d", _("Executor"), execp_index + 1);
|
||||
snprintf(buffer, sizeof(buffer), "%s %d", _("Executor"), execp_index + 1);
|
||||
|
||||
gtk_list_store_set(panel_items, &iter, itemsColName, buffer, -1);
|
||||
}
|
||||
@@ -5071,7 +5099,7 @@ void button_update_indices()
|
||||
{
|
||||
for (int i = 0; i < buttons->len; i++) {
|
||||
Button *button = &g_array_index(buttons, Button, i);
|
||||
sprintf(button->name, "%s %d", _("Button"), i + 1);
|
||||
snprintf(button->name, sizeof(button->name), "%s %d", _("Button"), i + 1);
|
||||
gtk_label_set_text(GTK_LABEL(button->page_label), button->name);
|
||||
}
|
||||
|
||||
@@ -5089,7 +5117,7 @@ void button_update_indices()
|
||||
button_index++;
|
||||
char buffer[256];
|
||||
buffer[0] = 0;
|
||||
sprintf(buffer, "%s %d", _("Button"), button_index + 1);
|
||||
snprintf(buffer, sizeof(buffer), "%s %d", _("Button"), button_index + 1);
|
||||
|
||||
gtk_list_store_set(panel_items, &iter, itemsColName, buffer, -1);
|
||||
}
|
||||
@@ -5140,8 +5168,8 @@ void create_systemtray(GtkWidget *parent)
|
||||
_("Specifies the order used to arrange the system tray icons. \n"
|
||||
"'Ascending' means that icons are sorted in ascending order of their window names. \n"
|
||||
"'Descending' means that icons are sorted in descending order of their window names. \n"
|
||||
"'Left to right' means that icons are always added to the left. \n"
|
||||
"'Right to left' means that icons are always added to the right."),
|
||||
"'Left to right' means that icons are always added to the right. \n"
|
||||
"'Right to left' means that icons are always added to the left."),
|
||||
NULL);
|
||||
|
||||
row++;
|
||||
@@ -5156,6 +5184,7 @@ void create_systemtray(GtkWidget *parent)
|
||||
gtk_widget_show(systray_monitor);
|
||||
gtk_table_attach(GTK_TABLE(table), systray_monitor, col, col + 1, row, row + 1, GTK_FILL, 0, 0, 0);
|
||||
col++;
|
||||
gtk_combo_box_append_text(GTK_COMBO_BOX(systray_monitor), _("Primary"));
|
||||
gtk_combo_box_append_text(GTK_COMBO_BOX(systray_monitor), _("1"));
|
||||
gtk_combo_box_append_text(GTK_COMBO_BOX(systray_monitor), _("2"));
|
||||
gtk_combo_box_append_text(GTK_COMBO_BOX(systray_monitor), _("3"));
|
||||
@@ -5419,6 +5448,19 @@ void create_battery(GtkWidget *parent)
|
||||
_("Command to be executed when the alert threshold is reached."),
|
||||
NULL);
|
||||
|
||||
row++, col = 2;
|
||||
label = gtk_label_new(_("Battery full command"));
|
||||
gtk_misc_set_alignment(GTK_MISC(label), 0, 0);
|
||||
gtk_widget_show(label);
|
||||
gtk_table_attach(GTK_TABLE(table), label, col, col + 1, row, row + 1, GTK_FILL, 0, 0, 0);
|
||||
col++;
|
||||
|
||||
battery_alert_full_cmd = gtk_entry_new();
|
||||
gtk_widget_show(battery_alert_full_cmd);
|
||||
gtk_entry_set_width_chars(GTK_ENTRY(battery_alert_full_cmd), 50);
|
||||
gtk_table_attach(GTK_TABLE(table), battery_alert_full_cmd, col, col + 3, row, row + 1, GTK_FILL, 0, 0, 0);
|
||||
col++;
|
||||
|
||||
change_paragraph(parent);
|
||||
|
||||
label = gtk_label_new(_("<b>AC connection events</b>"));
|
||||
@@ -5736,6 +5778,46 @@ void create_battery(GtkWidget *parent)
|
||||
_("Specifies the font clor used to display the battery text."),
|
||||
NULL);
|
||||
|
||||
row++, col = 2;
|
||||
label = gtk_label_new(_("First line format"));
|
||||
gtk_misc_set_alignment(GTK_MISC(label), 0, 0);
|
||||
gtk_widget_show(label);
|
||||
gtk_table_attach(GTK_TABLE(table), label, col, col + 1, row, row + 1, GTK_FILL, 0, 0, 0);
|
||||
col++;
|
||||
|
||||
battery_format1 = gtk_entry_new();
|
||||
gtk_widget_show(battery_format1);
|
||||
gtk_entry_set_width_chars(GTK_ENTRY(battery_format1), 16);
|
||||
gtk_table_attach(GTK_TABLE(table), battery_format1, col, col + 1, row, row + 1, GTK_FILL, 0, 0, 0);
|
||||
col++;
|
||||
char *bat_format_spec = "Format specification:\n"
|
||||
" %s: State (charging, discharging, full, unknown).\n"
|
||||
" %m: Minutes left until completely charged/discharged (estimated).\n"
|
||||
" %h: Hours left until completely charged/discharged (estimated).\n"
|
||||
" %t: Time left. Shows \"hrs:mins\" when charging/discharging, or \"Full\".\n"
|
||||
" %p: Percentage. Includes the % sign.";
|
||||
gtk_tooltips_set_tip(tooltips,
|
||||
battery_format1,
|
||||
_(bat_format_spec),
|
||||
NULL);
|
||||
|
||||
row++, col = 2;
|
||||
label = gtk_label_new(_("Second line format"));
|
||||
gtk_misc_set_alignment(GTK_MISC(label), 0, 0);
|
||||
gtk_widget_show(label);
|
||||
gtk_table_attach(GTK_TABLE(table), label, col, col + 1, row, row + 1, GTK_FILL, 0, 0, 0);
|
||||
col++;
|
||||
|
||||
battery_format2 = gtk_entry_new();
|
||||
gtk_widget_show(battery_format2);
|
||||
gtk_entry_set_width_chars(GTK_ENTRY(battery_format2), 16);
|
||||
gtk_table_attach(GTK_TABLE(table), battery_format2, col, col + 1, row, row + 1, GTK_FILL, 0, 0, 0);
|
||||
col++;
|
||||
gtk_tooltips_set_tip(tooltips,
|
||||
battery_format2,
|
||||
_(bat_format_spec),
|
||||
NULL);
|
||||
|
||||
change_paragraph(parent);
|
||||
}
|
||||
|
||||
|
||||
@@ -19,7 +19,7 @@ extern GtkWidget *panel_window_name, *disable_transparency;
|
||||
extern GtkWidget *panel_mouse_effects;
|
||||
extern GtkWidget *mouse_hover_icon_opacity, *mouse_hover_icon_saturation, *mouse_hover_icon_brightness;
|
||||
extern GtkWidget *mouse_pressed_icon_opacity, *mouse_pressed_icon_saturation, *mouse_pressed_icon_brightness;
|
||||
extern GtkWidget *panel_primary_monitor_first, *panel_shrink;
|
||||
extern GtkWidget *panel_shrink;
|
||||
|
||||
enum { itemsColName = 0, itemsColValue, itemsNumCols };
|
||||
extern GtkListStore *panel_items, *all_items;
|
||||
@@ -49,7 +49,7 @@ extern GtkWidget *panel_background;
|
||||
|
||||
// taskbar
|
||||
extern GtkWidget *taskbar_show_desktop, *taskbar_show_name, *taskbar_padding_x, *taskbar_padding_y, *taskbar_spacing;
|
||||
extern GtkWidget *taskbar_hide_inactive_tasks, *taskbar_hide_diff_monitor;
|
||||
extern GtkWidget *taskbar_hide_inactive_tasks, *taskbar_hide_diff_monitor, *taskbar_hide_diff_desktop;
|
||||
extern GtkWidget *taskbar_name_padding_x, *taskbar_name_padding_y, *taskbar_name_inactive_color,
|
||||
*taskbar_name_active_color;
|
||||
extern GtkWidget *taskbar_name_font, *taskbar_name_font_set;
|
||||
@@ -88,10 +88,10 @@ extern GtkWidget *clock_font_line1, *clock_font_line1_set, *clock_font_line2, *c
|
||||
extern GtkWidget *clock_background;
|
||||
|
||||
// battery
|
||||
extern GtkWidget *battery_hide_if_higher, *battery_alert_if_lower, *battery_alert_cmd;
|
||||
extern GtkWidget *battery_hide_if_higher, *battery_alert_if_lower, *battery_alert_cmd, *battery_alert_full_cmd;
|
||||
extern GtkWidget *battery_padding_x, *battery_padding_y;
|
||||
extern GtkWidget *battery_font_line1, *battery_font_line1_set, *battery_font_line2, *battery_font_line2_set,
|
||||
*battery_font_color;
|
||||
*battery_font_color, *battery_format1, *battery_format2;
|
||||
extern GtkWidget *battery_background;
|
||||
extern GtkWidget *battery_tooltip;
|
||||
extern GtkWidget *battery_left_command, *battery_mclick_command, *battery_right_command, *battery_uwheel_command,
|
||||
@@ -105,7 +105,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 +203,8 @@ enum {
|
||||
bgColBorderSidesBottom,
|
||||
bgColBorderSidesLeft,
|
||||
bgColBorderSidesRight,
|
||||
bgColFillWeight,
|
||||
bgColBorderWeight,
|
||||
bgNumCols
|
||||
};
|
||||
|
||||
@@ -211,7 +213,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 };
|
||||
|
||||
@@ -69,23 +69,23 @@ void config_read_file(const char *path)
|
||||
if (!config_has_panel_items) {
|
||||
char panel_items[256];
|
||||
panel_items[0] = 0;
|
||||
strcat(panel_items, "T");
|
||||
strlcat(panel_items, "T", sizeof(panel_items));
|
||||
if (config_has_battery) {
|
||||
if (config_battery_enabled)
|
||||
strcat(panel_items, "B");
|
||||
strlcat(panel_items, "B", sizeof(panel_items));
|
||||
} else {
|
||||
if (no_items_battery_enabled)
|
||||
strcat(panel_items, "B");
|
||||
strlcat(panel_items, "B", sizeof(panel_items));
|
||||
}
|
||||
if (config_has_systray) {
|
||||
if (config_systray_enabled)
|
||||
strcat(panel_items, "S");
|
||||
strlcat(panel_items, "S", sizeof(panel_items));
|
||||
} else {
|
||||
if (no_items_systray_enabled)
|
||||
strcat(panel_items, "S");
|
||||
strlcat(panel_items, "S", sizeof(panel_items));
|
||||
}
|
||||
if (no_items_clock_enabled)
|
||||
strcat(panel_items, "C");
|
||||
strlcat(panel_items, "C", sizeof(panel_items));
|
||||
set_panel_items(panel_items);
|
||||
}
|
||||
}
|
||||
@@ -161,6 +161,8 @@ void config_write_backgrounds(FILE *fp)
|
||||
|
||||
int r;
|
||||
int b;
|
||||
double fill_weight;
|
||||
double border_weight;
|
||||
gboolean sideTop;
|
||||
gboolean sideBottom;
|
||||
gboolean sideLeft;
|
||||
@@ -228,6 +230,10 @@ void config_write_backgrounds(FILE *fp)
|
||||
&sideLeft,
|
||||
bgColBorderSidesRight,
|
||||
&sideRight,
|
||||
bgColFillWeight,
|
||||
&fill_weight,
|
||||
bgColBorderWeight,
|
||||
&border_weight,
|
||||
-1);
|
||||
fprintf(fp, "# Background %d: %s\n", index, text ? text : "");
|
||||
fprintf(fp, "rounded = %d\n", r);
|
||||
@@ -236,15 +242,18 @@ void config_write_backgrounds(FILE *fp)
|
||||
char sides[10];
|
||||
sides[0] = '\0';
|
||||
if (sideTop)
|
||||
strcat(sides, "T");
|
||||
strlcat(sides, "T", sizeof(sides));
|
||||
if (sideBottom)
|
||||
strcat(sides, "B");
|
||||
strlcat(sides, "B", sizeof(sides));
|
||||
if (sideLeft)
|
||||
strcat(sides, "L");
|
||||
strlcat(sides, "L", sizeof(sides));
|
||||
if (sideRight)
|
||||
strcat(sides, "R");
|
||||
strlcat(sides, "R", sizeof(sides));
|
||||
fprintf(fp, "border_sides = %s\n", sides);
|
||||
|
||||
fprintf(fp, "border_content_tint_weight = %d\n", (int)(border_weight));
|
||||
fprintf(fp, "background_content_tint_weight = %d\n", (int)(fill_weight));
|
||||
|
||||
config_write_color(fp, "background_color", *fillColor, fillOpacity);
|
||||
config_write_color(fp, "border_color", *borderColor, borderOpacity);
|
||||
if (gradient_id >= 0)
|
||||
@@ -326,17 +335,15 @@ void config_write_panel(FILE *fp)
|
||||
fprintf(fp, "\n");
|
||||
|
||||
fprintf(fp, "panel_monitor = ");
|
||||
if (gtk_combo_box_get_active(GTK_COMBO_BOX(panel_combo_monitor)) == 0) {
|
||||
if (gtk_combo_box_get_active(GTK_COMBO_BOX(panel_combo_monitor)) <= 0) {
|
||||
fprintf(fp, "all");
|
||||
} else if (gtk_combo_box_get_active(GTK_COMBO_BOX(panel_combo_monitor)) == 1) {
|
||||
fprintf(fp, "primary");
|
||||
} else {
|
||||
fprintf(fp, "%d", gtk_combo_box_get_active(GTK_COMBO_BOX(panel_combo_monitor)));
|
||||
fprintf(fp, "%d", gtk_combo_box_get_active(GTK_COMBO_BOX(panel_combo_monitor)) - 1);
|
||||
}
|
||||
fprintf(fp, "\n");
|
||||
|
||||
fprintf(fp,
|
||||
"primary_monitor_first = %d\n",
|
||||
gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(panel_primary_monitor_first)) ? 1 : 0);
|
||||
|
||||
fprintf(fp, "panel_shrink = %d\n", gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(panel_shrink)) ? 1 : 0);
|
||||
|
||||
fprintf(fp, "autohide = %d\n", gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(panel_autohide)) ? 1 : 0);
|
||||
@@ -401,6 +408,9 @@ void config_write_taskbar(FILE *fp)
|
||||
fprintf(fp,
|
||||
"taskbar_hide_different_monitor = %d\n",
|
||||
gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(taskbar_hide_diff_monitor)) ? 1 : 0);
|
||||
fprintf(fp,
|
||||
"taskbar_hide_different_desktop = %d\n",
|
||||
gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(taskbar_hide_diff_desktop)) ? 1 : 0);
|
||||
fprintf(fp,
|
||||
"taskbar_always_show_all_desktop_tasks = %d\n",
|
||||
gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(taskbar_always_show_all_desktop_tasks)) ? 1 : 0);
|
||||
@@ -468,7 +478,7 @@ void config_write_task_font_color(FILE *fp, char *name, GtkWidget *task_color)
|
||||
GdkColor color;
|
||||
gtk_color_button_get_color(GTK_COLOR_BUTTON(task_color), &color);
|
||||
char full_name[128];
|
||||
sprintf(full_name, "task%s_font_color", name);
|
||||
snprintf(full_name, sizeof(full_name), "task%s_font_color", name);
|
||||
config_write_color(fp, full_name, color, gtk_color_button_get_alpha(GTK_COLOR_BUTTON(task_color)) * 100 / 0xffff);
|
||||
}
|
||||
|
||||
@@ -479,7 +489,7 @@ void config_write_task_icon_osb(FILE *fp,
|
||||
GtkWidget *widget_brightness)
|
||||
{
|
||||
char full_name[128];
|
||||
sprintf(full_name, "task%s_icon_asb", name);
|
||||
snprintf(full_name, sizeof(full_name), "task%s_icon_asb", name);
|
||||
fprintf(fp,
|
||||
"%s = %d %d %d\n",
|
||||
full_name,
|
||||
@@ -491,7 +501,7 @@ void config_write_task_icon_osb(FILE *fp,
|
||||
void config_write_task_background(FILE *fp, char *name, GtkWidget *task_background)
|
||||
{
|
||||
char full_name[128];
|
||||
sprintf(full_name, "task%s_background_id", name);
|
||||
snprintf(full_name, sizeof(full_name), "task%s_background_id", name);
|
||||
fprintf(fp, "%s = %d\n", full_name, gtk_combo_box_get_active(GTK_COMBO_BOX(task_background)));
|
||||
}
|
||||
|
||||
@@ -516,6 +526,11 @@ void config_write_task(FILE *fp)
|
||||
if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(task_font_set)))
|
||||
fprintf(fp, "task_font = %s\n", gtk_font_button_get_font_name(GTK_FONT_BUTTON(task_font)));
|
||||
fprintf(fp, "task_tooltip = %d\n", gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(tooltip_task_show)) ? 1 : 0);
|
||||
fprintf(fp, "task_thumbnail = %d\n", gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(tooltip_task_thumbnail)) ? 1 : 0);
|
||||
fprintf(fp,
|
||||
"task_thumbnail_size = %d\n",
|
||||
(int)gtk_spin_button_get_value(GTK_SPIN_BUTTON(tooltip_task_thumbnail_size)));
|
||||
|
||||
|
||||
// same for: "" _normal _active _urgent _iconified
|
||||
if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(task_default_color_set))) {
|
||||
@@ -629,7 +644,11 @@ void config_write_systray(FILE *fp)
|
||||
(int)gtk_spin_button_get_value(GTK_SPIN_BUTTON(systray_icon_brightness)));
|
||||
|
||||
fprintf(fp, "systray_monitor = ");
|
||||
fprintf(fp, "%d", MAX(1, 1 + gtk_combo_box_get_active(GTK_COMBO_BOX(systray_monitor))));
|
||||
if (gtk_combo_box_get_active(GTK_COMBO_BOX(systray_monitor)) <= 0) {
|
||||
fprintf(fp, "primary");
|
||||
} else {
|
||||
fprintf(fp, "%d", MAX(1, gtk_combo_box_get_active(GTK_COMBO_BOX(systray_monitor))));
|
||||
}
|
||||
fprintf(fp, "\n");
|
||||
|
||||
fprintf(fp, "systray_name_filter = %s\n", gtk_entry_get_text(GTK_ENTRY(systray_name_filter)));
|
||||
@@ -752,6 +771,7 @@ void config_write_battery(FILE *fp)
|
||||
fprintf(fp, "battery_tooltip = %d\n", gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(battery_tooltip)) ? 1 : 0);
|
||||
fprintf(fp, "battery_low_status = %g\n", gtk_spin_button_get_value(GTK_SPIN_BUTTON(battery_alert_if_lower)));
|
||||
fprintf(fp, "battery_low_cmd = %s\n", gtk_entry_get_text(GTK_ENTRY(battery_alert_cmd)));
|
||||
fprintf(fp, "battery_full_cmd = %s\n", gtk_entry_get_text(GTK_ENTRY(battery_alert_full_cmd)));
|
||||
if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(battery_font_line1_set)))
|
||||
fprintf(fp, "bat1_font = %s\n", gtk_font_button_get_font_name(GTK_FONT_BUTTON(battery_font_line1)));
|
||||
if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(battery_font_line2_set)))
|
||||
@@ -762,6 +782,8 @@ void config_write_battery(FILE *fp)
|
||||
"battery_font_color",
|
||||
color,
|
||||
gtk_color_button_get_alpha(GTK_COLOR_BUTTON(battery_font_color)) * 100 / 0xffff);
|
||||
fprintf(fp, "bat1_format = %s\n", gtk_entry_get_text(GTK_ENTRY(battery_format1)));
|
||||
fprintf(fp, "bat2_format = %s\n", gtk_entry_get_text(GTK_ENTRY(battery_format2)));
|
||||
fprintf(fp,
|
||||
"battery_padding = %d %d\n",
|
||||
(int)gtk_spin_button_get_value(GTK_SPIN_BUTTON(battery_padding_x)),
|
||||
@@ -973,7 +995,7 @@ unsigned short checksum_txt(FILE *f)
|
||||
|
||||
void config_save_file(const char *path)
|
||||
{
|
||||
printf("config_save_file : %s\n", path);
|
||||
fprintf(stderr, "tint2: config_save_file : %s\n", path);
|
||||
|
||||
FILE *fp;
|
||||
if ((fp = fopen(path, "w+t")) == NULL)
|
||||
@@ -1217,6 +1239,12 @@ void add_entry(char *key, char *value)
|
||||
int id = gradient_index_safe(atoi(value));
|
||||
gtk_combo_box_set_active(GTK_COMBO_BOX(background_gradient_press), id);
|
||||
background_force_update();
|
||||
} else if (strcmp(key, "border_content_tint_weight") == 0) {
|
||||
gtk_spin_button_set_value(GTK_SPIN_BUTTON(background_border_content_tint_weight), atoi(value));
|
||||
background_force_update();
|
||||
} else if (strcmp(key, "background_content_tint_weight") == 0) {
|
||||
gtk_spin_button_set_value(GTK_SPIN_BUTTON(background_fill_content_tint_weight), atoi(value));
|
||||
background_force_update();
|
||||
}
|
||||
|
||||
/* Panel */
|
||||
@@ -1351,20 +1379,20 @@ void add_entry(char *key, char *value)
|
||||
} else if (strcmp(key, "panel_monitor") == 0) {
|
||||
if (strcmp(value, "all") == 0)
|
||||
gtk_combo_box_set_active(GTK_COMBO_BOX(panel_combo_monitor), 0);
|
||||
else if (strcmp(value, "1") == 0)
|
||||
else if (strcmp(value, "primary") == 0)
|
||||
gtk_combo_box_set_active(GTK_COMBO_BOX(panel_combo_monitor), 1);
|
||||
else if (strcmp(value, "2") == 0)
|
||||
else if (strcmp(value, "1") == 0)
|
||||
gtk_combo_box_set_active(GTK_COMBO_BOX(panel_combo_monitor), 2);
|
||||
else if (strcmp(value, "3") == 0)
|
||||
else if (strcmp(value, "2") == 0)
|
||||
gtk_combo_box_set_active(GTK_COMBO_BOX(panel_combo_monitor), 3);
|
||||
else if (strcmp(value, "4") == 0)
|
||||
else if (strcmp(value, "3") == 0)
|
||||
gtk_combo_box_set_active(GTK_COMBO_BOX(panel_combo_monitor), 4);
|
||||
else if (strcmp(value, "5") == 0)
|
||||
else if (strcmp(value, "4") == 0)
|
||||
gtk_combo_box_set_active(GTK_COMBO_BOX(panel_combo_monitor), 5);
|
||||
else if (strcmp(value, "6") == 0)
|
||||
else if (strcmp(value, "5") == 0)
|
||||
gtk_combo_box_set_active(GTK_COMBO_BOX(panel_combo_monitor), 6);
|
||||
} else if (strcmp(key, "primary_monitor_first") == 0) {
|
||||
gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(panel_primary_monitor_first), atoi(value));
|
||||
else if (strcmp(value, "6") == 0)
|
||||
gtk_combo_box_set_active(GTK_COMBO_BOX(panel_combo_monitor), 7);
|
||||
} else if (strcmp(key, "panel_shrink") == 0) {
|
||||
gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(panel_shrink), atoi(value));
|
||||
}
|
||||
@@ -1403,12 +1431,18 @@ void add_entry(char *key, char *value)
|
||||
gtk_spin_button_set_value(GTK_SPIN_BUTTON(battery_alert_if_lower), atof(value));
|
||||
} else if (strcmp(key, "battery_low_cmd") == 0) {
|
||||
gtk_entry_set_text(GTK_ENTRY(battery_alert_cmd), value);
|
||||
} else if (strcmp(key, "battery_full_cmd") == 0) {
|
||||
gtk_entry_set_text(GTK_ENTRY(battery_alert_full_cmd), value);
|
||||
} else if (strcmp(key, "bat1_font") == 0) {
|
||||
gtk_font_button_set_font_name(GTK_FONT_BUTTON(battery_font_line1), value);
|
||||
gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(battery_font_line1_set), TRUE);
|
||||
} else if (strcmp(key, "bat2_font") == 0) {
|
||||
gtk_font_button_set_font_name(GTK_FONT_BUTTON(battery_font_line2), value);
|
||||
gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(battery_font_line2_set), TRUE);
|
||||
} else if (strcmp(key, "bat1_format") == 0) {
|
||||
gtk_entry_set_text(GTK_ENTRY(battery_format1), value);
|
||||
} else if (strcmp(key, "bat2_format") == 0) {
|
||||
gtk_entry_set_text(GTK_ENTRY(battery_format2), value);
|
||||
} else if (strcmp(key, "battery_font_color") == 0) {
|
||||
extract_values(value, &value1, &value2, &value3);
|
||||
GdkColor col;
|
||||
@@ -1551,6 +1585,8 @@ void add_entry(char *key, char *value)
|
||||
gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(taskbar_hide_inactive_tasks), atoi(value));
|
||||
} else if (strcmp(key, "taskbar_hide_different_monitor") == 0) {
|
||||
gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(taskbar_hide_diff_monitor), atoi(value));
|
||||
} else if (strcmp(key, "taskbar_hide_different_desktop") == 0) {
|
||||
gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(taskbar_hide_diff_desktop), atoi(value));
|
||||
} else if (strcmp(key, "taskbar_always_show_all_desktop_tasks") == 0) {
|
||||
gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(taskbar_always_show_all_desktop_tasks), atoi(value));
|
||||
} else if (strcmp(key, "taskbar_name_padding") == 0) {
|
||||
@@ -1716,6 +1752,10 @@ void add_entry(char *key, char *value)
|
||||
else if (strcmp(key, "task_tooltip") == 0 || strcmp(key, "tooltip") == 0) {
|
||||
gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(tooltip_task_show), atoi(value));
|
||||
}
|
||||
else if (strcmp(key, "task_thumbnail") == 0)
|
||||
gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(tooltip_task_thumbnail), atoi(value));
|
||||
else if (strcmp(key, "task_thumbnail_size") == 0)
|
||||
gtk_spin_button_set_value(GTK_SPIN_BUTTON(tooltip_task_thumbnail_size), MAX(8, atoi(value)));
|
||||
|
||||
/* Systray */
|
||||
else if (strcmp(key, "systray") == 0) {
|
||||
@@ -1747,18 +1787,20 @@ void add_entry(char *key, char *value)
|
||||
} else if (strcmp(key, "systray_icon_size") == 0) {
|
||||
gtk_spin_button_set_value(GTK_SPIN_BUTTON(systray_icon_size), atoi(value));
|
||||
} else if (strcmp(key, "systray_monitor") == 0) {
|
||||
if (strcmp(value, "1") == 0)
|
||||
if (strcmp(value, "primary") == 0)
|
||||
gtk_combo_box_set_active(GTK_COMBO_BOX(systray_monitor), 0);
|
||||
else if (strcmp(value, "2") == 0)
|
||||
else if (strcmp(value, "1") == 0)
|
||||
gtk_combo_box_set_active(GTK_COMBO_BOX(systray_monitor), 1);
|
||||
else if (strcmp(value, "3") == 0)
|
||||
else if (strcmp(value, "2") == 0)
|
||||
gtk_combo_box_set_active(GTK_COMBO_BOX(systray_monitor), 2);
|
||||
else if (strcmp(value, "4") == 0)
|
||||
else if (strcmp(value, "3") == 0)
|
||||
gtk_combo_box_set_active(GTK_COMBO_BOX(systray_monitor), 3);
|
||||
else if (strcmp(value, "5") == 0)
|
||||
else if (strcmp(value, "4") == 0)
|
||||
gtk_combo_box_set_active(GTK_COMBO_BOX(systray_monitor), 4);
|
||||
else if (strcmp(value, "6") == 0)
|
||||
else if (strcmp(value, "5") == 0)
|
||||
gtk_combo_box_set_active(GTK_COMBO_BOX(systray_monitor), 5);
|
||||
else if (strcmp(value, "6") == 0)
|
||||
gtk_combo_box_set_active(GTK_COMBO_BOX(systray_monitor), 6);
|
||||
} else if (strcmp(key, "systray_icon_asb") == 0) {
|
||||
extract_values(value, &value1, &value2, &value3);
|
||||
gtk_spin_button_set_value(GTK_SPIN_BUTTON(systray_icon_opacity), atoi(value1));
|
||||
|
||||
@@ -145,10 +145,16 @@ void theme_list_append(const gchar *path)
|
||||
GtkTreeIter iter;
|
||||
gtk_list_store_append(theme_list_store, &iter);
|
||||
|
||||
gchar *name = strrchr(path, '/') + 1;
|
||||
gchar *name, *dir;
|
||||
|
||||
gchar *dir = g_strdup(path);
|
||||
strrchr(dir, '/')[0] = 0;
|
||||
if (strchr(path, '/')) {
|
||||
name = strrchr(path, '/') + 1;
|
||||
dir = g_strdup(path);
|
||||
strrchr(dir, '/')[0] = 0;
|
||||
} else {
|
||||
name = (gchar*)path;
|
||||
dir = g_strdup(".");
|
||||
}
|
||||
char *suffix = contract_tilde(dir);
|
||||
g_free(dir);
|
||||
|
||||
@@ -171,7 +177,7 @@ gboolean update_snapshot(gpointer ignored)
|
||||
{
|
||||
gchar *tint2_cache_dir = g_build_filename(g_get_user_cache_dir(), "tint2", NULL);
|
||||
if (!g_file_test(tint2_cache_dir, G_FILE_TEST_IS_DIR))
|
||||
g_mkdir(tint2_cache_dir, 0700);
|
||||
g_mkdir_with_parents(tint2_cache_dir, 0700);
|
||||
g_free(tint2_cache_dir);
|
||||
}
|
||||
|
||||
@@ -201,7 +207,7 @@ gboolean update_snapshot(gpointer ignored)
|
||||
|
||||
char hash[MD4_HEX_SIZE + 4];
|
||||
md4hexf(path, hash);
|
||||
strcat(hash, ".png");
|
||||
strlcat(hash, ".png", sizeof(hash));
|
||||
|
||||
gchar *snap = g_build_filename(g_get_user_cache_dir(), "tint2", hash, NULL);
|
||||
pixbuf = force_refresh ? NULL : gdk_pixbuf_new_from_file(snap, NULL);
|
||||
|
||||
@@ -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,7 +149,7 @@ 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);
|
||||
@@ -152,16 +157,25 @@ void tooltip_update_geometry()
|
||||
|
||||
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 +
|
||||
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;
|
||||
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 +
|
||||
cairo_image_surface_get_width(g_tooltip.image);
|
||||
height += g_tooltip.paddingy + cairo_image_surface_get_height(g_tooltip.image);
|
||||
}
|
||||
|
||||
if (panel_horizontal && panel_position & BOTTOM)
|
||||
y = panel->posy - height;
|
||||
@@ -278,8 +292,16 @@ void tooltip_update()
|
||||
-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);
|
||||
pango_cairo_show_layout(c, layout);
|
||||
|
||||
g_object_unref(layout);
|
||||
|
||||
if (g_tooltip.image) {
|
||||
cairo_translate(c,
|
||||
left_bg_border_width(g_tooltip.bg) + g_tooltip.paddingx,
|
||||
height - bottom_bg_border_width(g_tooltip.bg) - g_tooltip.paddingy - 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 +309,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 +324,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;
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
313
src/util/area.c
313
src/util/area.c
@@ -224,7 +224,7 @@ int compute_desired_size(Area *a)
|
||||
if (a->_compute_desired_size)
|
||||
return a->_compute_desired_size(a);
|
||||
if (a->size_mode == LAYOUT_FIXED)
|
||||
fprintf(stderr, YELLOW "Area %s does not set desired size!" RESET "\n", a->name);
|
||||
fprintf(stderr, YELLOW "tint2: Area %s does not set desired size!" RESET "\n", a->name);
|
||||
return container_compute_desired_size(a);
|
||||
}
|
||||
|
||||
@@ -385,7 +385,7 @@ void draw_tree(Area *a)
|
||||
a->posx,
|
||||
a->posy);
|
||||
else
|
||||
fprintf(stderr, RED "%s %d: area %s has no pixmap!!!" RESET "\n", __FILE__, __LINE__, a->name);
|
||||
fprintf(stderr, RED "tint2: %s %d: area %s has no pixmap!!!" RESET "\n", __FILE__, __LINE__, a->name);
|
||||
|
||||
for (GList *l = a->children; l; l = l->next)
|
||||
draw_tree((Area *)l->data);
|
||||
@@ -490,28 +490,59 @@ void draw(Area *a)
|
||||
cairo_surface_destroy(cs);
|
||||
}
|
||||
|
||||
double tint_color_channel(double a, double b, double tint_weight)
|
||||
{
|
||||
double gamma = 2.2;
|
||||
if (tint_weight == 0.0)
|
||||
return a;
|
||||
double result = sqrt((1.-tint_weight)*pow(a, gamma) + tint_weight * pow(b, gamma));
|
||||
return result;
|
||||
}
|
||||
|
||||
void set_cairo_source_tinted(cairo_t *c, Color *color1, Color *color2, double tint_weight)
|
||||
{
|
||||
cairo_set_source_rgba(c,
|
||||
tint_color_channel(color1->rgb[0], color2->rgb[0], tint_weight),
|
||||
tint_color_channel(color1->rgb[1], color2->rgb[1], tint_weight),
|
||||
tint_color_channel(color1->rgb[2], color2->rgb[2], tint_weight),
|
||||
color1->alpha);
|
||||
}
|
||||
|
||||
void set_cairo_source_bg_color(Area *a, cairo_t *c)
|
||||
{
|
||||
Color content_color;
|
||||
if (a->_get_content_color)
|
||||
a->_get_content_color(a, &content_color);
|
||||
else
|
||||
bzero(&content_color, sizeof(content_color));
|
||||
if (a->mouse_state == MOUSE_OVER)
|
||||
set_cairo_source_tinted(c, &a->bg->fill_color_hover, &content_color, a->bg->fill_content_tint_weight);
|
||||
else if (a->mouse_state == MOUSE_DOWN)
|
||||
set_cairo_source_tinted(c, &a->bg->fill_color_pressed, &content_color, a->bg->fill_content_tint_weight);
|
||||
else
|
||||
set_cairo_source_tinted(c, &a->bg->fill_color, &content_color, a->bg->fill_content_tint_weight);
|
||||
}
|
||||
|
||||
void set_cairo_source_border_color(Area *a, cairo_t *c)
|
||||
{
|
||||
Color content_color;
|
||||
if (a->_get_content_color)
|
||||
a->_get_content_color(a, &content_color);
|
||||
else
|
||||
bzero(&content_color, sizeof(content_color));
|
||||
if (a->mouse_state == MOUSE_OVER)
|
||||
set_cairo_source_tinted(c, &a->bg->border_color_hover, &content_color, a->bg->border_content_tint_weight);
|
||||
else if (a->mouse_state == MOUSE_DOWN)
|
||||
set_cairo_source_tinted(c, &a->bg->border_color_pressed, &content_color, a->bg->border_content_tint_weight);
|
||||
else
|
||||
set_cairo_source_tinted(c, &a->bg->border.color, &content_color, a->bg->border_content_tint_weight);
|
||||
}
|
||||
|
||||
void draw_background(Area *a, cairo_t *c)
|
||||
{
|
||||
if ((a->bg->fill_color.alpha > 0.0) ||
|
||||
(panel_config.mouse_effects && (a->has_mouse_over_effect || a->has_mouse_press_effect))) {
|
||||
if (a->mouse_state == MOUSE_OVER)
|
||||
cairo_set_source_rgba(c,
|
||||
a->bg->fill_color_hover.rgb[0],
|
||||
a->bg->fill_color_hover.rgb[1],
|
||||
a->bg->fill_color_hover.rgb[2],
|
||||
a->bg->fill_color_hover.alpha);
|
||||
else if (a->mouse_state == MOUSE_DOWN)
|
||||
cairo_set_source_rgba(c,
|
||||
a->bg->fill_color_pressed.rgb[0],
|
||||
a->bg->fill_color_pressed.rgb[1],
|
||||
a->bg->fill_color_pressed.rgb[2],
|
||||
a->bg->fill_color_pressed.alpha);
|
||||
else
|
||||
cairo_set_source_rgba(c,
|
||||
a->bg->fill_color.rgb[0],
|
||||
a->bg->fill_color.rgb[1],
|
||||
a->bg->fill_color.rgb[2],
|
||||
a->bg->fill_color.alpha);
|
||||
|
||||
// Not sure about this
|
||||
draw_rect(c,
|
||||
left_border_width(a),
|
||||
@@ -519,7 +550,7 @@ void draw_background(Area *a, cairo_t *c)
|
||||
a->width - left_right_border_width(a),
|
||||
a->height - top_bottom_border_width(a),
|
||||
a->bg->border.radius - a->bg->border.width / 1.571);
|
||||
|
||||
set_cairo_source_bg_color(a, c);
|
||||
cairo_fill(c);
|
||||
}
|
||||
for (GList *l = a->gradient_instances_by_state[a->mouse_state]; l; l = l->next) {
|
||||
@@ -540,24 +571,7 @@ void draw_background(Area *a, cairo_t *c)
|
||||
cairo_set_line_width(c, a->bg->border.width);
|
||||
|
||||
// draw border inside (x, y, width, height)
|
||||
if (a->mouse_state == MOUSE_OVER)
|
||||
cairo_set_source_rgba(c,
|
||||
a->bg->border_color_hover.rgb[0],
|
||||
a->bg->border_color_hover.rgb[1],
|
||||
a->bg->border_color_hover.rgb[2],
|
||||
a->bg->border_color_hover.alpha);
|
||||
else if (a->mouse_state == MOUSE_DOWN)
|
||||
cairo_set_source_rgba(c,
|
||||
a->bg->border_color_pressed.rgb[0],
|
||||
a->bg->border_color_pressed.rgb[1],
|
||||
a->bg->border_color_pressed.rgb[2],
|
||||
a->bg->border_color_pressed.alpha);
|
||||
else
|
||||
cairo_set_source_rgba(c,
|
||||
a->bg->border.color.rgb[0],
|
||||
a->bg->border.color.rgb[1],
|
||||
a->bg->border.color.rgb[2],
|
||||
a->bg->border.color.alpha);
|
||||
set_cairo_source_border_color(a, c);
|
||||
draw_rect_on_sides(c,
|
||||
left_border_width(a) / 2.,
|
||||
top_border_width(a) / 2.,
|
||||
@@ -630,7 +644,7 @@ void free_area(Area *a)
|
||||
free_area_gradient_instances(a);
|
||||
}
|
||||
|
||||
void mouse_over(Area *area, int pressed)
|
||||
void mouse_over(Area *area, gboolean pressed)
|
||||
{
|
||||
if (mouse_over_area == area && !area)
|
||||
return;
|
||||
@@ -843,14 +857,14 @@ int top_bottom_bg_border_width(Background *bg)
|
||||
|
||||
void area_dump_geometry(Area *area, int indent)
|
||||
{
|
||||
fprintf(stderr, "%*s%s:\n", indent, "", area->name);
|
||||
fprintf(stderr, "tint2: %*s%s:\n", indent, "", area->name);
|
||||
indent += 2;
|
||||
if (!area->on_screen) {
|
||||
fprintf(stderr, "%*shidden\n", indent, "");
|
||||
fprintf(stderr, "tint2: %*shidden\n", indent, "");
|
||||
return;
|
||||
}
|
||||
fprintf(stderr,
|
||||
"%*sBox: x = %d, y = %d, w = %d, h = %d, desired size = %d\n",
|
||||
"tint2: %*sBox: x = %d, y = %d, w = %d, h = %d, desired size = %d\n",
|
||||
indent,
|
||||
"",
|
||||
area->posx,
|
||||
@@ -859,7 +873,7 @@ void area_dump_geometry(Area *area, int indent)
|
||||
area->height,
|
||||
compute_desired_size(area));
|
||||
fprintf(stderr,
|
||||
"%*sBorder: left = %d, right = %d, top = %d, bottom = %d\n",
|
||||
"tint2: %*sBorder: left = %d, right = %d, top = %d, bottom = %d\n",
|
||||
indent,
|
||||
"",
|
||||
left_border_width(area),
|
||||
@@ -867,7 +881,7 @@ void area_dump_geometry(Area *area, int indent)
|
||||
top_border_width(area),
|
||||
bottom_border_width(area));
|
||||
fprintf(stderr,
|
||||
"%*sPadding: left = right = %d, top = bottom = %d, spacing = %d\n",
|
||||
"tint2: %*sPadding: left = right = %d, top = bottom = %d, spacing = %d\n",
|
||||
indent,
|
||||
"",
|
||||
area->paddingxlr,
|
||||
@@ -876,13 +890,214 @@ void area_dump_geometry(Area *area, int indent)
|
||||
if (area->_dump_geometry)
|
||||
area->_dump_geometry(area, indent);
|
||||
if (area->children) {
|
||||
fprintf(stderr, "%*sChildren:\n", indent, "");
|
||||
fprintf(stderr, "tint2: %*sChildren:\n", indent, "");
|
||||
indent += 2;
|
||||
for (GList *l = area->children; l; l = l->next)
|
||||
area_dump_geometry((Area *)l->data, indent);
|
||||
}
|
||||
}
|
||||
|
||||
void area_compute_available_size(Area *area,
|
||||
int *available_w,
|
||||
int *available_h)
|
||||
{
|
||||
Panel *panel = (Panel *)area->panel;
|
||||
if (panel_horizontal) {
|
||||
*available_w = panel->area.width;
|
||||
*available_h = area->height - 2 * area->paddingy - left_right_border_width(area);
|
||||
} else {
|
||||
*available_w = area->width - 2 * area->paddingxlr - left_right_border_width(area);
|
||||
*available_h = panel->area.height;
|
||||
}
|
||||
}
|
||||
|
||||
void area_compute_inner_size(Area *area,
|
||||
int *inner_w,
|
||||
int *inner_h)
|
||||
{
|
||||
if (panel_horizontal) {
|
||||
*inner_w = area->width - 2 * area->paddingxlr - left_right_border_width(area);
|
||||
*inner_h = area->height - 2 * area->paddingy - top_bottom_border_width(area);
|
||||
} else {
|
||||
*inner_w = area->width - 2 * area->paddingxlr - left_right_border_width(area);
|
||||
*inner_h = area->height - 2 * area->paddingy - top_bottom_border_width(area);
|
||||
}
|
||||
}
|
||||
|
||||
void area_compute_text_geometry(Area *area,
|
||||
const char *line1,
|
||||
const char *line2,
|
||||
PangoFontDescription *line1_font_desc,
|
||||
PangoFontDescription *line2_font_desc,
|
||||
int *line1_height_ink,
|
||||
int *line1_height,
|
||||
int *line1_width,
|
||||
int *line2_height_ink,
|
||||
int *line2_height,
|
||||
int *line2_width)
|
||||
{
|
||||
int available_w, available_h;
|
||||
area_compute_available_size(area, &available_w, &available_h);
|
||||
|
||||
if (line1 && line1[0])
|
||||
get_text_size2(line1_font_desc,
|
||||
line1_height_ink,
|
||||
line1_height,
|
||||
line1_width,
|
||||
available_h,
|
||||
available_w,
|
||||
line1,
|
||||
strlen(line1),
|
||||
PANGO_WRAP_WORD_CHAR,
|
||||
PANGO_ELLIPSIZE_NONE,
|
||||
FALSE);
|
||||
else
|
||||
*line1_width = *line1_height_ink = *line1_height = 0;
|
||||
|
||||
if (line2 && line2[0])
|
||||
get_text_size2(line2_font_desc,
|
||||
line2_height_ink,
|
||||
line2_height,
|
||||
line2_width,
|
||||
available_h,
|
||||
available_w,
|
||||
line2,
|
||||
strlen(line2),
|
||||
PANGO_WRAP_WORD_CHAR,
|
||||
PANGO_ELLIPSIZE_NONE,
|
||||
FALSE);
|
||||
else
|
||||
*line2_width = *line2_height_ink = *line2_height = 0;
|
||||
}
|
||||
|
||||
int text_area_compute_desired_size(Area *area,
|
||||
const char *line1,
|
||||
const char *line2,
|
||||
PangoFontDescription *line1_font_desc,
|
||||
PangoFontDescription *line2_font_desc)
|
||||
{
|
||||
int line1_height_ink, line1_height, line1_width, line2_height_ink, 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);
|
||||
|
||||
if (panel_horizontal) {
|
||||
int new_size = MAX(line1_width, line2_width) + 2 * area->paddingxlr + left_right_border_width(area);
|
||||
return new_size;
|
||||
} else {
|
||||
int new_size = line1_height + line2_height + 2 * area->paddingy + top_bottom_border_width(area);
|
||||
return new_size;
|
||||
}
|
||||
}
|
||||
|
||||
gboolean resize_text_area(Area *area,
|
||||
const char *line1,
|
||||
const char *line2,
|
||||
PangoFontDescription *line1_font_desc,
|
||||
PangoFontDescription *line2_font_desc,
|
||||
int *line1_posy,
|
||||
int *line2_posy)
|
||||
{
|
||||
gboolean result = FALSE;
|
||||
|
||||
schedule_redraw(area);
|
||||
|
||||
int line1_height_ink, line1_height, line1_width;
|
||||
int line2_height_ink, 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);
|
||||
|
||||
int new_size = text_area_compute_desired_size(area,
|
||||
line1,
|
||||
line2,
|
||||
line1_font_desc,
|
||||
line2_font_desc);
|
||||
if (panel_horizontal) {
|
||||
if (new_size != area->width) {
|
||||
if (new_size < area->width && abs(new_size - area->width) < 6) {
|
||||
// we try to limit the number of resizes
|
||||
new_size = area->width;
|
||||
} else {
|
||||
area->width = new_size;
|
||||
}
|
||||
*line1_posy = (area->height - line1_height) / 2;
|
||||
if (line2) {
|
||||
*line1_posy -= (line2_height) / 2;
|
||||
*line2_posy = *line1_posy + line1_height;
|
||||
}
|
||||
result = TRUE;
|
||||
}
|
||||
} else {
|
||||
if (new_size != area->height) {
|
||||
area->height = new_size;
|
||||
*line1_posy = (area->height - line1_height) / 2;
|
||||
if (line2) {
|
||||
*line1_posy -= (line2_height) / 2;
|
||||
*line2_posy = *line1_posy + line1_height;
|
||||
}
|
||||
result = TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
void draw_text_area(Area *area,
|
||||
cairo_t *c,
|
||||
const char *line1,
|
||||
const char *line2,
|
||||
PangoFontDescription *line1_font_desc,
|
||||
PangoFontDescription *line2_font_desc,
|
||||
int line1_posy,
|
||||
int line2_posy,
|
||||
Color *color)
|
||||
{
|
||||
int inner_w, inner_h;
|
||||
area_compute_inner_size(area, &inner_w, &inner_h);
|
||||
|
||||
PangoLayout *layout = pango_cairo_create_layout(c);
|
||||
pango_layout_set_alignment(layout, PANGO_ALIGN_CENTER);
|
||||
pango_layout_set_wrap(layout, PANGO_WRAP_WORD_CHAR);
|
||||
pango_layout_set_ellipsize(layout, PANGO_ELLIPSIZE_NONE);
|
||||
pango_layout_set_width(layout, inner_w * PANGO_SCALE);
|
||||
pango_layout_set_height(layout, inner_h * PANGO_SCALE);
|
||||
cairo_set_source_rgba(c, color->rgb[0], color->rgb[1], color->rgb[2], color->alpha);
|
||||
|
||||
if (line1 && line1[0]) {
|
||||
pango_layout_set_font_description(layout, line1_font_desc);
|
||||
pango_layout_set_text(layout, line1, strlen(line1));
|
||||
pango_cairo_update_layout(c, layout);
|
||||
draw_text(layout, c, (area->width - inner_w) / 2, line1_posy, color, ((Panel *)area->panel)->font_shadow);
|
||||
}
|
||||
|
||||
if (line2 && line2[0]) {
|
||||
pango_layout_set_font_description(layout, line2_font_desc);
|
||||
pango_layout_set_indent(layout, 0);
|
||||
pango_layout_set_text(layout, line2, strlen(line2));
|
||||
pango_cairo_update_layout(c, layout);
|
||||
draw_text(layout, c, (area->width - inner_w) / 2, line2_posy, color, ((Panel *)area->panel)->font_shadow);
|
||||
}
|
||||
|
||||
g_object_unref(layout);
|
||||
}
|
||||
|
||||
Area *compute_element_area(Area *area, Element element)
|
||||
{
|
||||
if (element == ELEMENT_SELF)
|
||||
@@ -955,7 +1170,7 @@ void free_gradient_instance(GradientInstance *gi)
|
||||
void instantiate_area_gradients(Area *area)
|
||||
{
|
||||
if (debug_gradients)
|
||||
fprintf(stderr, "Initializing gradients for area %s\n", area->name);
|
||||
fprintf(stderr, "tint2: Initializing gradients for area %s\n", area->name);
|
||||
for (int i = 0; i < MOUSE_STATE_COUNT; i++) {
|
||||
g_assert_null(area->gradient_instances_by_state[i]);
|
||||
GradientClass *g = area->bg->gradients[i];
|
||||
@@ -970,7 +1185,7 @@ void instantiate_area_gradients(Area *area)
|
||||
void free_area_gradient_instances(Area *area)
|
||||
{
|
||||
if (debug_gradients)
|
||||
fprintf(stderr, "Freeing gradients for area %s\n", area->name);
|
||||
fprintf(stderr, "tint2: Freeing gradients for area %s\n", area->name);
|
||||
for (int i = 0; i < MOUSE_STATE_COUNT; i++) {
|
||||
for (GList *l = area->gradient_instances_by_state[i]; l; l = l->next) {
|
||||
GradientInstance *gi = (GradientInstance *)l->data;
|
||||
@@ -996,7 +1211,7 @@ double compute_control_point_offset(Area *area, Offset *offset)
|
||||
double height = element_area->height;
|
||||
double radius = sqrt(element_area->width * element_area->width + element_area->height * element_area->height) / 2.0;
|
||||
|
||||
double left, top;
|
||||
double left = 0, top = 0;
|
||||
if (offset->element == ELEMENT_SELF) {
|
||||
left = 0;
|
||||
top = 0;
|
||||
|
||||
@@ -154,6 +154,8 @@ typedef struct Background {
|
||||
Color border_color_pressed;
|
||||
// Pointer to a GradientClass or NULL, no ownership
|
||||
GradientClass *gradients[MOUSE_STATE_COUNT];
|
||||
double fill_content_tint_weight;
|
||||
double border_content_tint_weight;
|
||||
} Background;
|
||||
|
||||
typedef enum Layout {
|
||||
@@ -233,6 +235,7 @@ typedef struct Area {
|
||||
// Returns a copy of the tooltip to be displayed for this widget.
|
||||
// The caller takes ownership of the pointer.
|
||||
char *(*_get_tooltip_text)(void *obj);
|
||||
cairo_surface_t *(*_get_tooltip_image)(void *obj);
|
||||
|
||||
// Returns true if the Area handles a mouse event at the given x, y coordinates relative to the window.
|
||||
// Leave this to NULL to use a default implementation.
|
||||
@@ -240,6 +243,8 @@ typedef struct Area {
|
||||
|
||||
// Prints the geometry of the object on stderr, with left indentation of indent spaces.
|
||||
void (*_dump_geometry)(void *obj, int indent);
|
||||
|
||||
void (*_get_content_color)(void *obj, Color *color);
|
||||
} Area;
|
||||
|
||||
// Initializes the Background member to default values.
|
||||
@@ -263,6 +268,39 @@ int relayout_with_constraint(Area *a, int maximum_size);
|
||||
int compute_desired_size(Area *a);
|
||||
int container_compute_desired_size(Area *a);
|
||||
|
||||
void area_compute_text_geometry(Area *area,
|
||||
const char *line1,
|
||||
const char *line2,
|
||||
PangoFontDescription *line1_font_desc,
|
||||
PangoFontDescription *line2_font_desc,
|
||||
int *line1_height_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,
|
||||
const char *line1,
|
||||
const char *line2,
|
||||
PangoFontDescription *line1_font_desc,
|
||||
PangoFontDescription *line2_font_desc);
|
||||
gboolean resize_text_area(Area *area,
|
||||
const char *line1,
|
||||
const char *line2,
|
||||
PangoFontDescription *line1_font_desc,
|
||||
PangoFontDescription *line2_font_desc,
|
||||
int *line1_posy,
|
||||
int *line2_posy);
|
||||
void draw_text_area(Area *area,
|
||||
cairo_t *c,
|
||||
const char *line1,
|
||||
const char *line2,
|
||||
PangoFontDescription *line1_font_desc,
|
||||
PangoFontDescription *line2_font_desc,
|
||||
int line1_posy,
|
||||
int line2_posy,
|
||||
Color *color);
|
||||
|
||||
int left_border_width(Area *a);
|
||||
int right_border_width(Area *a);
|
||||
int left_right_border_width(Area *a);
|
||||
@@ -324,7 +362,7 @@ void free_area_gradient_instances(Area *area);
|
||||
|
||||
void area_dump_geometry(Area *area, int indent);
|
||||
|
||||
void mouse_over(Area *area, int pressed);
|
||||
void mouse_over(Area *area, gboolean pressed);
|
||||
void mouse_out();
|
||||
|
||||
void update_gradient(GradientInstance *gi);
|
||||
|
||||
17
src/util/bool.h
Normal file
17
src/util/bool.h
Normal 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
|
||||
@@ -117,14 +117,14 @@ void save_cache(Cache *cache, const gchar *cache_path)
|
||||
fd = open(cache_path, O_RDONLY | O_CREAT, 0600);
|
||||
}
|
||||
if (fd == -1) {
|
||||
fprintf(stderr, RED "Could not save icon theme cache!" RESET "\n");
|
||||
fprintf(stderr, RED "tint2: Could not save icon theme cache!" RESET "\n");
|
||||
return;
|
||||
}
|
||||
flock(fd, LOCK_EX);
|
||||
|
||||
FILE *f = fopen(cache_path, "w");
|
||||
if (!f) {
|
||||
fprintf(stderr, RED "Could not save icon theme cache!" RESET "\n");
|
||||
fprintf(stderr, RED "tint2: Could not save icon theme cache!" RESET "\n");
|
||||
goto unlock;
|
||||
}
|
||||
g_hash_table_foreach(cache->_table, write_cache_line, f);
|
||||
|
||||
10
src/util/colors.h
Normal file
10
src/util/colors.h
Normal 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
|
||||
@@ -31,20 +31,180 @@
|
||||
#include <glib.h>
|
||||
#include <glib/gstdio.h>
|
||||
#include "common.h"
|
||||
#include "../server.h"
|
||||
#include "server.h"
|
||||
#include <sys/wait.h>
|
||||
#include <sys/types.h>
|
||||
#include <pwd.h>
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
#include <sys/time.h>
|
||||
#include <errno.h>
|
||||
#include <dirent.h>
|
||||
#if !defined(__OpenBSD__)
|
||||
#include <wordexp.h>
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_RSVG
|
||||
#include <librsvg/rsvg.h>
|
||||
#endif
|
||||
|
||||
#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"
|
||||
|
||||
void write_string(int fd, const char *s)
|
||||
{
|
||||
int len = strlen(s);
|
||||
while (len > 0) {
|
||||
int count = write(fd, s, len);
|
||||
if (count >= 0) {
|
||||
s += count;
|
||||
len -= count;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void log_string(int fd, const char *s)
|
||||
{
|
||||
write_string(2, s);
|
||||
write_string(fd, s);
|
||||
}
|
||||
|
||||
void dump_backtrace(int log_fd)
|
||||
{
|
||||
#ifndef DISABLE_BACKTRACE
|
||||
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);
|
||||
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.
|
||||
void safe_sleep(int seconds)
|
||||
{
|
||||
double t0 = get_time();
|
||||
while (1) {
|
||||
double t = get_time();
|
||||
if (t > t0 + seconds)
|
||||
return;
|
||||
sleep(1);
|
||||
}
|
||||
}
|
||||
|
||||
const char *signal_name(int sig)
|
||||
{
|
||||
switch (sig) {
|
||||
case SIGHUP:
|
||||
return "SIGHUP: Hangup (POSIX).";
|
||||
case SIGINT:
|
||||
return "SIGINT: Interrupt (ANSI).";
|
||||
case SIGQUIT:
|
||||
return "SIGQUIT: Quit (POSIX).";
|
||||
case SIGILL:
|
||||
return "SIGILL: Illegal instruction (ANSI).";
|
||||
case SIGTRAP:
|
||||
return "SIGTRAP: Trace trap (POSIX).";
|
||||
case SIGABRT:
|
||||
return "SIGABRT/SIGIOT: Abort (ANSI) / IOT trap (4.2 BSD).";
|
||||
case SIGBUS:
|
||||
return "SIGBUS: BUS error (4.2 BSD).";
|
||||
case SIGFPE:
|
||||
return "SIGFPE: Floating-point exception (ANSI).";
|
||||
case SIGKILL:
|
||||
return "SIGKILL: Kill, unblockable (POSIX).";
|
||||
case SIGUSR1:
|
||||
return "SIGUSR1: User-defined signal 1 (POSIX).";
|
||||
case SIGSEGV:
|
||||
return "SIGSEGV: Segmentation violation (ANSI).";
|
||||
case SIGUSR2:
|
||||
return "SIGUSR2: User-defined signal 2 (POSIX).";
|
||||
case SIGPIPE:
|
||||
return "SIGPIPE: Broken pipe (POSIX).";
|
||||
case SIGALRM:
|
||||
return "SIGALRM: Alarm clock (POSIX).";
|
||||
case SIGTERM:
|
||||
return "SIGTERM: Termination (ANSI).";
|
||||
// case SIGSTKFLT: return "SIGSTKFLT: Stack fault.";
|
||||
case SIGCHLD:
|
||||
return "SIGCHLD: Child status has changed (POSIX).";
|
||||
case SIGCONT:
|
||||
return "SIGCONT: Continue (POSIX).";
|
||||
case SIGSTOP:
|
||||
return "SIGSTOP: Stop, unblockable (POSIX).";
|
||||
case SIGTSTP:
|
||||
return "SIGTSTP: Keyboard stop (POSIX).";
|
||||
case SIGTTIN:
|
||||
return "SIGTTIN: Background read from tty (POSIX).";
|
||||
case SIGTTOU:
|
||||
return "SIGTTOU: Background write to tty (POSIX).";
|
||||
case SIGURG:
|
||||
return "SIGURG: Urgent condition on socket (4.2 BSD).";
|
||||
case SIGXCPU:
|
||||
return "SIGXCPU: CPU limit exceeded (4.2 BSD).";
|
||||
case SIGXFSZ:
|
||||
return "SIGXFSZ: File size limit exceeded (4.2 BSD).";
|
||||
case SIGVTALRM:
|
||||
return "SIGVTALRM: Virtual alarm clock (4.2 BSD).";
|
||||
case SIGPROF:
|
||||
return "SIGPROF: Profiling alarm clock (4.2 BSD).";
|
||||
// case SIGPWR: return "SIGPWR: Power failure restart (System V).";
|
||||
case SIGSYS:
|
||||
return "SIGSYS: Bad system call.";
|
||||
}
|
||||
static char s[64];
|
||||
snprintf(s, sizeof(s), "SIG=%d: Unknown", sig);
|
||||
return s;
|
||||
}
|
||||
|
||||
const char *get_home_dir()
|
||||
{
|
||||
const char *s = getenv("HOME");
|
||||
if (s)
|
||||
return s;
|
||||
struct passwd *pw = getpwuid(getuid());
|
||||
if (!pw)
|
||||
return NULL;
|
||||
return pw->pw_dir;
|
||||
}
|
||||
|
||||
void copy_file(const char *path_src, const char *path_dest)
|
||||
{
|
||||
@@ -67,7 +227,7 @@ void copy_file(const char *path_src, const char *path_dest)
|
||||
|
||||
while ((nb = fread(buffer, 1, sizeof(buffer), file_src)) > 0) {
|
||||
if (nb != fwrite(buffer, 1, nb, file_dest)) {
|
||||
printf("Error while copying file %s to %s\n", path_src, path_dest);
|
||||
fprintf(stderr, "tint2: Error while copying file %s to %s\n", path_src, path_dest);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -103,8 +263,23 @@ gboolean parse_line(const char *line, char **key, char **value)
|
||||
|
||||
extern char *config_path;
|
||||
|
||||
int setenvd(const char *name, const int value)
|
||||
{
|
||||
char buf[256];
|
||||
snprintf(buf, sizeof(buf), "%d", value);
|
||||
return setenv(name, buf, 1);
|
||||
}
|
||||
|
||||
#ifndef TINT2CONF
|
||||
pid_t tint_exec(const char *command, const char *dir, const char *tooltip, Time time, Area *area, int x, int y)
|
||||
pid_t tint_exec(const char *command,
|
||||
const char *dir,
|
||||
const char *tooltip,
|
||||
Time time,
|
||||
Area *area,
|
||||
int x,
|
||||
int y,
|
||||
gboolean terminal,
|
||||
gboolean startup_notification)
|
||||
{
|
||||
if (!command || strlen(command) == 0)
|
||||
return -1;
|
||||
@@ -172,43 +347,23 @@ pid_t tint_exec(const char *command, const char *dir, const char *tooltip, Time
|
||||
panel_y2 = panel->posy + panel->area.height;
|
||||
}
|
||||
|
||||
command = g_strdup_printf("export TINT2_CONFIG=%s;"
|
||||
"export TINT2_BUTTON_X=%d;"
|
||||
"export TINT2_BUTTON_Y=%d;"
|
||||
"export TINT2_BUTTON_W=%d;"
|
||||
"export TINT2_BUTTON_H=%d;"
|
||||
"export TINT2_BUTTON_ALIGNED_X=%d;"
|
||||
"export TINT2_BUTTON_ALIGNED_Y=%d;"
|
||||
"export TINT2_BUTTON_ALIGNED_X1=%d;"
|
||||
"export TINT2_BUTTON_ALIGNED_Y1=%d;"
|
||||
"export TINT2_BUTTON_ALIGNED_X2=%d;"
|
||||
"export TINT2_BUTTON_ALIGNED_Y2=%d;"
|
||||
"export TINT2_BUTTON_PANEL_X1=%d;"
|
||||
"export TINT2_BUTTON_PANEL_Y1=%d;"
|
||||
"export TINT2_BUTTON_PANEL_X2=%d;"
|
||||
"export TINT2_BUTTON_PANEL_Y2=%d;"
|
||||
"%s",
|
||||
config_path,
|
||||
x,
|
||||
y,
|
||||
area->width,
|
||||
area->height,
|
||||
aligned_x,
|
||||
aligned_y,
|
||||
aligned_x1,
|
||||
aligned_y1,
|
||||
aligned_x2,
|
||||
aligned_y2,
|
||||
panel_x1,
|
||||
panel_y1,
|
||||
panel_x2,
|
||||
panel_y2,
|
||||
command);
|
||||
setenv("TINT2_CONFIG", config_path, 1);
|
||||
setenvd("TINT2_BUTTON_X", x);
|
||||
setenvd("TINT2_BUTTON_Y", y);
|
||||
setenvd("TINT2_BUTTON_W", area->width);
|
||||
setenvd("TINT2_BUTTON_H", area->height);
|
||||
setenvd("TINT2_BUTTON_ALIGNED_X", aligned_x);
|
||||
setenvd("TINT2_BUTTON_ALIGNED_Y", aligned_y);
|
||||
setenvd("TINT2_BUTTON_ALIGNED_X1", aligned_x1);
|
||||
setenvd("TINT2_BUTTON_ALIGNED_Y1", aligned_y1);
|
||||
setenvd("TINT2_BUTTON_ALIGNED_X2", aligned_x2);
|
||||
setenvd("TINT2_BUTTON_ALIGNED_Y2", aligned_y2);
|
||||
setenvd("TINT2_BUTTON_PANEL_X1", panel_x1);
|
||||
setenvd("TINT2_BUTTON_PANEL_Y1", panel_y1);
|
||||
setenvd("TINT2_BUTTON_PANEL_X2", panel_x2);
|
||||
setenvd("TINT2_BUTTON_PANEL_Y2", panel_y2);
|
||||
} else {
|
||||
command = g_strdup_printf("export TINT2_CONFIG=%s;"
|
||||
"%s",
|
||||
config_path,
|
||||
command);
|
||||
setenv("TINT2_CONFIG", config_path, 1);
|
||||
}
|
||||
|
||||
if (!command)
|
||||
@@ -219,7 +374,7 @@ pid_t tint_exec(const char *command, const char *dir, const char *tooltip, Time
|
||||
|
||||
#if HAVE_SN
|
||||
SnLauncherContext *ctx = 0;
|
||||
if (startup_notifications && time) {
|
||||
if (startup_notifications && startup_notification && time) {
|
||||
ctx = sn_launcher_context_new(server.sn_display, server.screen);
|
||||
sn_launcher_context_set_name(ctx, tooltip);
|
||||
sn_launcher_context_set_description(ctx, "Application launched from tint2");
|
||||
@@ -230,11 +385,11 @@ pid_t tint_exec(const char *command, const char *dir, const char *tooltip, Time
|
||||
pid_t pid;
|
||||
pid = fork();
|
||||
if (pid < 0) {
|
||||
fprintf(stderr, "Could not fork\n");
|
||||
fprintf(stderr, "tint2: Could not fork\n");
|
||||
} else if (pid == 0) {
|
||||
// Child process
|
||||
#if HAVE_SN
|
||||
if (startup_notifications && time) {
|
||||
if (startup_notifications && startup_notification && time) {
|
||||
sn_launcher_context_setup_child_process(ctx);
|
||||
}
|
||||
#endif // HAVE_SN
|
||||
@@ -243,10 +398,27 @@ pid_t tint_exec(const char *command, const char *dir, const char *tooltip, Time
|
||||
// Run the command
|
||||
if (dir)
|
||||
chdir(dir);
|
||||
execl("/bin/sh", "/bin/sh", "-c", command, NULL);
|
||||
fprintf(stderr, "Failed to execlp %s\n", command);
|
||||
close_all_fds();
|
||||
reset_signals();
|
||||
if (terminal) {
|
||||
#if !defined(__OpenBSD__)
|
||||
fprintf(stderr, "tint2: executing in x-terminal-emulator: %s\n", command);
|
||||
wordexp_t words;
|
||||
words.we_offs = 2;
|
||||
if (wordexp(command, &words, WRDE_DOOFFS | WRDE_SHOWERR) == 0) {
|
||||
words.we_wordv[0] = (char *)"x-terminal-emulator";
|
||||
words.we_wordv[1] = (char *)"-e";
|
||||
execvp("x-terminal-emulator", words.we_wordv);
|
||||
}
|
||||
#endif
|
||||
fprintf(stderr,
|
||||
"tint2: could not execute command in x-terminal-emulator: %s, executting in shell\n",
|
||||
command);
|
||||
}
|
||||
execlp("sh", "sh", "-c", command, NULL);
|
||||
fprintf(stderr, "tint2: Failed to execute %s\n", command);
|
||||
#if HAVE_SN
|
||||
if (startup_notifications && time) {
|
||||
if (startup_notifications && startup_notification && time) {
|
||||
sn_launcher_context_unref(ctx);
|
||||
}
|
||||
#endif // HAVE_SN
|
||||
@@ -254,17 +426,34 @@ pid_t tint_exec(const char *command, const char *dir, const char *tooltip, Time
|
||||
} else {
|
||||
// Parent process
|
||||
#if HAVE_SN
|
||||
if (startup_notifications && time) {
|
||||
if (startup_notifications && startup_notification && time) {
|
||||
g_tree_insert(server.pids, GINT_TO_POINTER(pid), ctx);
|
||||
}
|
||||
#endif // HAVE_SN
|
||||
}
|
||||
|
||||
unsetenv("TINT2_CONFIG");
|
||||
unsetenv("TINT2_BUTTON_X");
|
||||
unsetenv("TINT2_BUTTON_Y");
|
||||
unsetenv("TINT2_BUTTON_W");
|
||||
unsetenv("TINT2_BUTTON_H");
|
||||
unsetenv("TINT2_BUTTON_ALIGNED_X");
|
||||
unsetenv("TINT2_BUTTON_ALIGNED_Y");
|
||||
unsetenv("TINT2_BUTTON_ALIGNED_X1");
|
||||
unsetenv("TINT2_BUTTON_ALIGNED_Y1");
|
||||
unsetenv("TINT2_BUTTON_ALIGNED_X2");
|
||||
unsetenv("TINT2_BUTTON_ALIGNED_Y2");
|
||||
unsetenv("TINT2_BUTTON_PANEL_X1");
|
||||
unsetenv("TINT2_BUTTON_PANEL_Y1");
|
||||
unsetenv("TINT2_BUTTON_PANEL_X2");
|
||||
unsetenv("TINT2_BUTTON_PANEL_Y2");
|
||||
|
||||
return pid;
|
||||
}
|
||||
|
||||
void tint_exec_no_sn(const char *command)
|
||||
{
|
||||
tint_exec(command, NULL, NULL, 0, NULL, 0, 0);
|
||||
tint_exec(command, NULL, NULL, 0, NULL, 0, 0, FALSE, FALSE);
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -272,9 +461,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);
|
||||
@@ -287,14 +477,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 {
|
||||
@@ -604,7 +796,7 @@ Imlib_Image load_image(const char *path, int cached)
|
||||
}
|
||||
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.png", (int)getpid());
|
||||
int fd = open(tmp_filename, O_CREAT | O_EXCL, 0600);
|
||||
if (fd >= 0) {
|
||||
// We fork here because librsvg allocates memory like crazy
|
||||
@@ -615,13 +807,13 @@ Imlib_Image load_image(const char *path, int cached)
|
||||
RsvgHandle *svg = rsvg_handle_new_from_file(path, &err);
|
||||
|
||||
if (err != NULL) {
|
||||
fprintf(stderr, "Could not load svg image!: %s", err->message);
|
||||
fprintf(stderr, "tint2: Could not load svg image!: %s", err->message);
|
||||
g_error_free(err);
|
||||
} else {
|
||||
GdkPixbuf *pixbuf = rsvg_handle_get_pixbuf(svg);
|
||||
gdk_pixbuf_save(pixbuf, tmp_filename, "png", NULL, NULL);
|
||||
}
|
||||
exit(0);
|
||||
_exit(0);
|
||||
} else {
|
||||
// Parent
|
||||
close(fd);
|
||||
@@ -732,46 +924,48 @@ void clear_pixmap(Pixmap p, int x, int y, int w, int h)
|
||||
XRenderFreePicture(server.display, pict);
|
||||
}
|
||||
|
||||
void get_text_size2(PangoFontDescription *font,
|
||||
void get_text_size2(const PangoFontDescription *font,
|
||||
int *height_ink,
|
||||
int *height,
|
||||
int *width,
|
||||
int panel_height,
|
||||
int panel_width,
|
||||
char *text,
|
||||
int len,
|
||||
int available_height,
|
||||
int available_width,
|
||||
const char *text,
|
||||
int text_len,
|
||||
PangoWrapMode wrap,
|
||||
PangoEllipsizeMode ellipsis,
|
||||
gboolean markup)
|
||||
{
|
||||
PangoRectangle rect_ink, rect;
|
||||
|
||||
Pixmap pmap = XCreatePixmap(server.display, server.root_win, panel_height, panel_width, server.depth);
|
||||
available_width = MAX(0, available_width);
|
||||
available_height = MAX(0, available_height);
|
||||
|
||||
cairo_surface_t *cs = cairo_xlib_surface_create(server.display, pmap, server.visual, panel_height, panel_width);
|
||||
cairo_surface_t *cs =
|
||||
cairo_image_surface_create(CAIRO_FORMAT_ARGB32, available_height, available_width);
|
||||
cairo_t *c = cairo_create(cs);
|
||||
|
||||
PangoLayout *layout = pango_cairo_create_layout(c);
|
||||
pango_layout_set_width(layout, panel_width * PANGO_SCALE);
|
||||
pango_layout_set_height(layout, panel_height * PANGO_SCALE);
|
||||
pango_layout_set_width(layout, available_width * PANGO_SCALE);
|
||||
pango_layout_set_height(layout, available_height * PANGO_SCALE);
|
||||
pango_layout_set_wrap(layout, wrap);
|
||||
pango_layout_set_ellipsize(layout, ellipsis);
|
||||
pango_layout_set_font_description(layout, font);
|
||||
text_len = MAX(0, text_len);
|
||||
if (!markup)
|
||||
pango_layout_set_text(layout, text, len);
|
||||
pango_layout_set_text(layout, text, text_len);
|
||||
else
|
||||
pango_layout_set_markup(layout, text, len);
|
||||
pango_layout_set_markup(layout, text, text_len);
|
||||
|
||||
pango_layout_get_pixel_extents(layout, &rect_ink, &rect);
|
||||
*height_ink = rect_ink.height;
|
||||
*height = rect.height;
|
||||
*width = rect.width;
|
||||
// printf("dimension : %d - %d\n", rect_ink.height, rect.height);
|
||||
// fprintf(stderr, "tint2: dimension : %d - %d\n", rect_ink.height, rect.height);
|
||||
|
||||
g_object_unref(layout);
|
||||
cairo_destroy(c);
|
||||
cairo_surface_destroy(cs);
|
||||
XFreePixmap(server.display, pmap);
|
||||
}
|
||||
|
||||
#if !GLIB_CHECK_VERSION(2, 34, 0)
|
||||
@@ -841,3 +1035,142 @@ gint cmp_ptr(gconstpointer a, gconstpointer b)
|
||||
else
|
||||
return 1;
|
||||
}
|
||||
|
||||
void close_all_fds()
|
||||
{
|
||||
long maxfd = sysconf(_SC_OPEN_MAX);
|
||||
for (int fd = 3; fd < maxfd; fd++) {
|
||||
close(fd);
|
||||
}
|
||||
}
|
||||
|
||||
GString *tint2_g_string_replace(GString *s, const char *from, const char *to)
|
||||
{
|
||||
GString *result = g_string_new("");
|
||||
for (char *p = s->str; *p;) {
|
||||
if (strstr(p, from) == p) {
|
||||
g_string_append(result, to);
|
||||
p += strlen(from);
|
||||
} else {
|
||||
g_string_append_c(result, *p);
|
||||
p += 1;
|
||||
}
|
||||
}
|
||||
g_string_assign(s, result->str);
|
||||
g_string_free(result, TRUE);
|
||||
return s;
|
||||
}
|
||||
|
||||
void get_image_mean_color(const Imlib_Image image, Color *mean_color)
|
||||
{
|
||||
bzero(mean_color, sizeof(*mean_color));
|
||||
|
||||
if (!image)
|
||||
return;
|
||||
imlib_context_set_image(image);
|
||||
imlib_image_set_has_alpha(1);
|
||||
size_t size = (size_t)imlib_image_get_width() * (size_t)imlib_image_get_height();
|
||||
DATA32 *data = imlib_image_get_data_for_reading_only();
|
||||
DATA32 sum_r, sum_g, sum_b, count;
|
||||
sum_r = sum_g = sum_b = count = 0;
|
||||
for (size_t i = 0; i < size; i++) {
|
||||
DATA32 argb, a, r, g, b;
|
||||
argb = data[i];
|
||||
a = (argb >> 24) & 0xff;
|
||||
r = (argb >> 16) & 0xff;
|
||||
g = (argb >> 8) & 0xff;
|
||||
b = (argb) & 0xff;
|
||||
if (a) {
|
||||
sum_r += r;
|
||||
sum_g += g;
|
||||
sum_b += b;
|
||||
count++;
|
||||
}
|
||||
}
|
||||
|
||||
if (!count)
|
||||
count = 1;
|
||||
mean_color->alpha = 1.0;
|
||||
mean_color->rgb[0] = sum_r / 255.0 / count;
|
||||
mean_color->rgb[1] = sum_g / 255.0 / count;
|
||||
mean_color->rgb[2] = sum_b / 255.0 / count;
|
||||
}
|
||||
|
||||
void adjust_color(Color *color, int alpha, int saturation, int brightness)
|
||||
{
|
||||
if (alpha == 100 && saturation == 0 && brightness == 0)
|
||||
return;
|
||||
DATA32 argb = (((DATA32)(color->alpha * 255) & 0xff) << 24) |
|
||||
(((DATA32)(color->rgb[0] * 255) & 0xff) << 16) |
|
||||
(((DATA32)(color->rgb[1] * 255) & 0xff) << 8) |
|
||||
(((DATA32)(color->rgb[2] * 255) & 0xff) << 0);
|
||||
adjust_asb(&argb, 1, 1, alpha / 100.0, saturation / 100.0, brightness / 100.0);
|
||||
DATA32 a = (argb >> 24) & 0xff;
|
||||
DATA32 r = (argb >> 16) & 0xff;
|
||||
DATA32 g = (argb >> 8) & 0xff;
|
||||
DATA32 b = (argb) & 0xff;
|
||||
color->alpha = a / 255.;
|
||||
color->rgb[0] = r / 255.;
|
||||
color->rgb[1] = g / 255.;
|
||||
color->rgb[2] = b / 255.;
|
||||
}
|
||||
|
||||
void dump_image_data(const char *file_name, const char *name)
|
||||
{
|
||||
Imlib_Image image = load_image(file_name, false);
|
||||
if (!image) {
|
||||
fprintf(stderr, "tint2: Could not load image from file\n");
|
||||
return;
|
||||
}
|
||||
|
||||
gchar *header_name = g_strdup_printf("%s.h", name);
|
||||
gchar *guard = g_strdup_printf("%s_h", name);
|
||||
FILE *header = fopen(header_name, "wt");
|
||||
fprintf(header,
|
||||
"#ifndef %s\n"
|
||||
"#define %s\n"
|
||||
"\n"
|
||||
"#include <Imlib2.h>\n"
|
||||
"\n"
|
||||
"extern int %s_width;\n"
|
||||
"extern int %s_height;\n"
|
||||
"extern DATA32 %s_data[];\n"
|
||||
"\n"
|
||||
"#endif\n",
|
||||
guard,
|
||||
guard,
|
||||
name,
|
||||
name,
|
||||
name);
|
||||
fclose(header);
|
||||
g_free(guard);
|
||||
g_free(header_name);
|
||||
|
||||
imlib_context_set_image(image);
|
||||
|
||||
gchar *source_name = g_strdup_printf("%s.c", name);
|
||||
FILE *source = fopen(source_name, "wt");
|
||||
fprintf(source,
|
||||
"#include <%s.h>\n"
|
||||
"\n"
|
||||
"int %s_width = %d;\n"
|
||||
"int %s_height = %d;\n"
|
||||
"DATA32 %s_data[] = {",
|
||||
name,
|
||||
name,
|
||||
imlib_image_get_width(),
|
||||
name,
|
||||
imlib_image_get_height(),
|
||||
name);
|
||||
|
||||
size_t size = (size_t)imlib_image_get_width() * (size_t)imlib_image_get_height();
|
||||
DATA32 *data = imlib_image_get_data_for_reading_only();
|
||||
for (size_t i = 0; i < size; i++) {
|
||||
fprintf(source, "%s%u", i == 0 ? "" : ", ", data[i]);
|
||||
}
|
||||
fprintf(source, "};\n");
|
||||
fclose(source);
|
||||
g_free(source_name);
|
||||
|
||||
imlib_free_image();
|
||||
}
|
||||
|
||||
@@ -12,12 +12,8 @@
|
||||
#include <Imlib2.h>
|
||||
#include <pango/pangocairo.h>
|
||||
#include "area.h"
|
||||
|
||||
#define GREEN "\033[1;32m"
|
||||
#define YELLOW "\033[1;33m"
|
||||
#define RED "\033[1;31m"
|
||||
#define BLUE "\033[1;34m"
|
||||
#define RESET "\033[0m"
|
||||
#include "colors.h"
|
||||
#include "strlcat.h"
|
||||
|
||||
#define MAX3(a, b, c) MAX(MAX(a, b), c)
|
||||
#define MIN3(a, b, c) MIN(MIN(a, b), c)
|
||||
@@ -41,6 +37,18 @@ typedef enum MouseAction {
|
||||
|
||||
#define ALL_DESKTOPS 0xFFFFFFFF
|
||||
|
||||
void write_string(int fd, const char *s);
|
||||
void log_string(int fd, const char *s);
|
||||
|
||||
void dump_backtrace(int log_fd);
|
||||
|
||||
// sleep() returns early when signals arrive. This function does not.
|
||||
void safe_sleep(int seconds);
|
||||
|
||||
const char *signal_name(int sig);
|
||||
|
||||
const char *get_home_dir();
|
||||
|
||||
// Copies a file to another path
|
||||
void copy_file(const char *path_src, const char *path_dest);
|
||||
|
||||
@@ -54,8 +62,17 @@ void extract_values(const char *value, char **value1, char **value2, char **valu
|
||||
void extract_values_4(const char *value, char **value1, char **value2, char **value3, char **value4);
|
||||
|
||||
// Executes a command in a shell.
|
||||
pid_t tint_exec(const char *command, const char *dir, const char *tooltip, Time time, Area *area, int x, int y);
|
||||
pid_t tint_exec(const char *command,
|
||||
const char *dir,
|
||||
const char *tooltip,
|
||||
Time time,
|
||||
Area *area,
|
||||
int x,
|
||||
int y,
|
||||
gboolean terminal,
|
||||
gboolean startup_notification);
|
||||
void tint_exec_no_sn(const char *command);
|
||||
int setenvd(const char *name, const int value);
|
||||
|
||||
// Returns a copy of s in which "~" is expanded to the path to the user's home directory.
|
||||
// The caller takes ownership of the string.
|
||||
@@ -88,20 +105,21 @@ Imlib_Image load_image(const char *path, int cached);
|
||||
// * 1 = white
|
||||
void adjust_asb(DATA32 *data, int w, int h, float alpha_adjust, float satur_adjust, float bright_adjust);
|
||||
Imlib_Image adjust_icon(Imlib_Image original, int alpha, int saturation, int brightness);
|
||||
void adjust_color(Color *color, int alpha, int saturation, int brightness);
|
||||
|
||||
void create_heuristic_mask(DATA32 *data, int w, int h);
|
||||
|
||||
// Renders the current Imlib image to a drawable. Wrapper around imlib_render_image_on_drawable.
|
||||
void render_image(Drawable d, int x, int y);
|
||||
|
||||
void get_text_size2(PangoFontDescription *font,
|
||||
void get_text_size2(const PangoFontDescription *font,
|
||||
int *height_ink,
|
||||
int *height,
|
||||
int *width,
|
||||
int panel_height,
|
||||
int panel_with,
|
||||
char *text,
|
||||
int len,
|
||||
int available_height,
|
||||
int available_with,
|
||||
const char *text,
|
||||
int text_len,
|
||||
PangoWrapMode wrap,
|
||||
PangoEllipsizeMode ellipsis,
|
||||
gboolean markup);
|
||||
@@ -115,6 +133,8 @@ void draw_rect_on_sides(cairo_t *c, double x, double y, double w, double h, doub
|
||||
// Clears the pixmap (with transparent color)
|
||||
void clear_pixmap(Pixmap p, int x, int y, int w, int h);
|
||||
|
||||
void close_all_fds();
|
||||
|
||||
// Appends to the list locations all the directories contained in the environment variable var (split by ":").
|
||||
// Optional suffixes are added to each directory. The suffix arguments MUST end with NULL.
|
||||
// Returns the new value of the list.
|
||||
@@ -125,6 +145,12 @@ GSList *slist_remove_duplicates(GSList *list, GCompareFunc eq, GDestroyNotify fr
|
||||
// A trivial pointer comparator.
|
||||
gint cmp_ptr(gconstpointer a, gconstpointer b);
|
||||
|
||||
GString *tint2_g_string_replace(GString *s, const char *from, const char *to);
|
||||
|
||||
void get_image_mean_color(const Imlib_Image image, Color *mean_color);
|
||||
|
||||
void dump_image_data(const char *file_name, const char *name);
|
||||
|
||||
#define free_and_null(p) \
|
||||
{ \
|
||||
free(p); \
|
||||
|
||||
97
src/util/fps_distribution.c
Normal file
97
src/util/fps_distribution.c
Normal file
@@ -0,0 +1,97 @@
|
||||
/**************************************************************************
|
||||
*
|
||||
* Copyright (C) 2017 tint2 authors
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License version 2
|
||||
* as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
**************************************************************************/
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "fps_distribution.h"
|
||||
|
||||
static float *fps_distribution = NULL;
|
||||
|
||||
void init_fps_distribution()
|
||||
{
|
||||
// measure FPS with resolution:
|
||||
// 0-59: 1 (60 samples)
|
||||
// 60-199: 10 (14)
|
||||
// 200-1,999: 25 (72)
|
||||
// 1k-19,999: 1000 (19)
|
||||
// 20x+: inf (1)
|
||||
// => 166 samples
|
||||
if (fps_distribution)
|
||||
return;
|
||||
fps_distribution = calloc(170, sizeof(float));
|
||||
}
|
||||
|
||||
void cleanup_fps_distribution()
|
||||
{
|
||||
free(fps_distribution);
|
||||
fps_distribution = NULL;
|
||||
}
|
||||
|
||||
void sample_fps(double fps)
|
||||
{
|
||||
int fps_rounded = (int)(fps + 0.5);
|
||||
int i = 1;
|
||||
if (fps_rounded < 60) {
|
||||
i += fps_rounded;
|
||||
} else {
|
||||
i += 60;
|
||||
if (fps_rounded < 200) {
|
||||
i += (fps_rounded - 60) / 10;
|
||||
} else {
|
||||
i += 14;
|
||||
if (fps_rounded < 2000) {
|
||||
i += (fps_rounded - 200) / 25;
|
||||
} else {
|
||||
i += 72;
|
||||
if (fps_rounded < 20000) {
|
||||
i += (fps_rounded - 2000) / 1000;
|
||||
} else {
|
||||
i += 20;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// fprintf(stderr, "tint2: fps = %.0f => i = %d\n", fps, i);
|
||||
fps_distribution[i] += 1.;
|
||||
fps_distribution[0] += 1.;
|
||||
}
|
||||
|
||||
void fps_compute_stats(double *low, double *median, double *high, double *samples)
|
||||
{
|
||||
*median = *low = *high = *samples = -1;
|
||||
if (!fps_distribution || fps_distribution[0] < 1)
|
||||
return;
|
||||
float total = fps_distribution[0];
|
||||
*samples = (double)fps_distribution[0];
|
||||
float cum_low = 0.05f * total;
|
||||
float cum_median = 0.5f * total;
|
||||
float cum_high = 0.95f * total;
|
||||
float cum = 0;
|
||||
for (int i = 1; i <= 166; i++) {
|
||||
double value =
|
||||
(i < 60) ? i : (i < 74) ? (60 + (i - 60) * 10) : (i < 146) ? (200 + (i - 74) * 25)
|
||||
: (i < 165) ? (2000 + (i - 146) * 1000) : 20000;
|
||||
// fprintf(stderr, "tint2: %6.0f (i = %3d) : %.0f | ", value, i, (double)fps_distribution[i]);
|
||||
cum += fps_distribution[i];
|
||||
if (*low < 0 && cum >= cum_low)
|
||||
*low = value;
|
||||
if (*median < 0 && cum >= cum_median)
|
||||
*median = value;
|
||||
if (*high < 0 && cum >= cum_high)
|
||||
*high = value;
|
||||
}
|
||||
}
|
||||
9
src/util/fps_distribution.h
Normal file
9
src/util/fps_distribution.h
Normal file
@@ -0,0 +1,9 @@
|
||||
#ifndef FPS_DISTRIBUTION_H
|
||||
#define FPS_DISTRIBUTION_H
|
||||
|
||||
void init_fps_distribution();
|
||||
void cleanup_fps_distribution();
|
||||
void sample_fps(double fps);
|
||||
void fps_compute_stats(double *low, double *median, double *high, double *samples);
|
||||
|
||||
#endif
|
||||
@@ -40,7 +40,7 @@ GradientType gradient_type_from_string(const char *str)
|
||||
return GRADIENT_VERTICAL;
|
||||
if (g_str_equal(str, "radial"))
|
||||
return GRADIENT_CENTERED;
|
||||
fprintf(stderr, RED "Invalid gradient type: %s" RESET "\n", str);
|
||||
fprintf(stderr, RED "tint2: Invalid gradient type: %s" RESET "\n", str);
|
||||
return GRADIENT_VERTICAL;
|
||||
}
|
||||
|
||||
|
||||
@@ -67,11 +67,10 @@ void cleanup_gradient(GradientClass *g);
|
||||
// Gradient instances associated to Areas
|
||||
|
||||
struct Area;
|
||||
typedef struct Area Area;
|
||||
|
||||
typedef struct GradientInstance {
|
||||
GradientClass *gradient_class;
|
||||
Area *area;
|
||||
struct Area *area;
|
||||
cairo_pattern_t *pattern;
|
||||
} GradientInstance;
|
||||
|
||||
|
||||
83
src/util/print.c
Normal file
83
src/util/print.c
Normal 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
61
src/util/print.h
Normal 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
|
||||
@@ -18,21 +18,24 @@
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
**************************************************************************/
|
||||
|
||||
#include <X11/extensions/Xdamage.h>
|
||||
#include <X11/extensions/Xrender.h>
|
||||
#include <X11/extensions/Xrandr.h>
|
||||
|
||||
#include <stdio.h>
|
||||
#include <fcntl.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "server.h"
|
||||
#include "common.h"
|
||||
#include "config.h"
|
||||
#include "server.h"
|
||||
#include "signals.h"
|
||||
#include "window.h"
|
||||
|
||||
Server server;
|
||||
|
||||
gboolean primary_monitor_first = FALSE;
|
||||
|
||||
void server_catch_error(Display *d, XErrorEvent *ev)
|
||||
{
|
||||
}
|
||||
@@ -120,6 +123,14 @@ void server_init_atoms()
|
||||
server.atom.TARGETS = XInternAtom(server.display, "TARGETS", False);
|
||||
}
|
||||
|
||||
const char *GetAtomName(Display *disp, Atom a)
|
||||
{
|
||||
if (a == None)
|
||||
return "None";
|
||||
else
|
||||
return XGetAtomName(disp, a);
|
||||
}
|
||||
|
||||
void cleanup_server()
|
||||
{
|
||||
if (server.colormap)
|
||||
@@ -250,7 +261,7 @@ void get_root_pixmap()
|
||||
server.root_pmap = ret;
|
||||
|
||||
if (server.root_pmap == None) {
|
||||
fprintf(stderr, "tint2 : pixmap background detection failed\n");
|
||||
fprintf(stderr, "tint2: pixmap background detection failed\n");
|
||||
} else {
|
||||
XGCValues gcv;
|
||||
gcv.ts_x_origin = 0;
|
||||
@@ -268,13 +279,6 @@ int compare_monitor_pos(const void *monitor1, const void *monitor2)
|
||||
const Monitor *m1 = (const Monitor *)monitor1;
|
||||
const Monitor *m2 = (const Monitor *)monitor2;
|
||||
|
||||
if (primary_monitor_first) {
|
||||
if (m1->primary && !m2->primary)
|
||||
return -1;
|
||||
if (!m1->primary && m2->primary)
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (m1->x < m2->x) {
|
||||
return -1;
|
||||
} else if (m1->x > m2->x) {
|
||||
@@ -317,14 +321,14 @@ void get_monitors()
|
||||
|
||||
if (res && res->ncrtc >= num_monitors) {
|
||||
// use xrandr to identify monitors (does not work with proprietery nvidia drivers)
|
||||
printf("xRandr: Found crtc's: %d\n", res->ncrtc);
|
||||
fprintf(stderr, "tint2: xRandr: Found crtc's: %d\n", res->ncrtc);
|
||||
server.monitors = calloc(res->ncrtc, sizeof(Monitor));
|
||||
num_monitors = 0;
|
||||
for (int i = 0; i < res->ncrtc; ++i) {
|
||||
XRRCrtcInfo *crtc_info = XRRGetCrtcInfo(server.display, res, res->crtcs[i]);
|
||||
// Ignore empty crtc
|
||||
if (!crtc_info->width || !crtc_info->height) {
|
||||
printf("xRandr: crtc %d seems disabled\n", i);
|
||||
fprintf(stderr, "tint2: xRandr: crtc %d seems disabled\n", i);
|
||||
XRRFreeCrtcInfo(crtc_info);
|
||||
continue;
|
||||
}
|
||||
@@ -337,7 +341,7 @@ void get_monitors()
|
||||
server.monitors[i_monitor].names = calloc((crtc_info->noutput + 1), sizeof(gchar *));
|
||||
for (int j = 0; j < crtc_info->noutput; ++j) {
|
||||
XRROutputInfo *output_info = XRRGetOutputInfo(server.display, res, crtc_info->outputs[j]);
|
||||
printf("xRandr: Linking output %s with crtc %d\n", output_info->name, i);
|
||||
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;
|
||||
@@ -394,7 +398,7 @@ void get_monitors()
|
||||
|
||||
void print_monitors()
|
||||
{
|
||||
fprintf(stderr, "Number of monitors: %d\n", server.num_monitors);
|
||||
fprintf(stderr, "tint2: Number of monitors: %d\n", server.num_monitors);
|
||||
for (int i = 0; i < server.num_monitors; i++) {
|
||||
fprintf(stderr,
|
||||
"Monitor %d: x = %d, y = %d, w = %d, h = %d\n",
|
||||
@@ -516,10 +520,10 @@ int get_current_desktop()
|
||||
|
||||
int ncols = x_screen_width / work_area_width;
|
||||
|
||||
// fprintf(stderr, "\n");
|
||||
// fprintf(stderr, "Work area size: %d x %d\n", work_area_width, work_area_height);
|
||||
// fprintf(stderr, "Viewport pos: %d x %d\n", viewport_x, viewport_y);
|
||||
// fprintf(stderr, "Viewport i: %d\n", (viewport_y / work_area_height) * ncols + viewport_x / work_area_width);
|
||||
// fprintf(stderr, "tint2: \n");
|
||||
// fprintf(stderr, "tint2: Work area size: %d x %d\n", work_area_width, work_area_height);
|
||||
// fprintf(stderr, "tint2: Viewport pos: %d x %d\n", viewport_x, viewport_y);
|
||||
// fprintf(stderr, "tint2: Viewport i: %d\n", (viewport_y / work_area_height) * ncols + viewport_x / work_area_width);
|
||||
|
||||
int result = (viewport_y / work_area_height) * ncols + viewport_x / work_area_width;
|
||||
return MAX(0, MIN(server.num_desktops - 1, result));
|
||||
@@ -550,7 +554,7 @@ void get_desktops()
|
||||
}
|
||||
if (server.num_desktops == 0) {
|
||||
server.num_desktops = 1;
|
||||
fprintf(stderr, "warning : WM doesn't respect NETWM specs. tint2 default to 1 desktop.\n");
|
||||
fprintf(stderr, "tint2: warning : WM doesn't respect NETWM specs. tint2 default to 1 desktop.\n");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -594,15 +598,77 @@ void server_init_visual()
|
||||
|
||||
server.real_transparency = TRUE;
|
||||
server.depth = 32;
|
||||
printf("real transparency on... depth: %d\n", server.depth);
|
||||
fprintf(stderr, "tint2: real transparency on... depth: %d\n", server.depth);
|
||||
server.colormap = XCreateColormap(server.display, server.root_win, visual, AllocNone);
|
||||
server.visual = visual;
|
||||
} else {
|
||||
// no composite manager or snapshot mode => fake transparency
|
||||
server.real_transparency = FALSE;
|
||||
server.depth = DefaultDepth(server.display, server.screen);
|
||||
printf("real transparency off.... depth: %d\n", server.depth);
|
||||
fprintf(stderr, "tint2: real transparency off.... depth: %d\n", server.depth);
|
||||
server.colormap = DefaultColormap(server.display, server.screen);
|
||||
server.visual = DefaultVisual(server.display, server.screen);
|
||||
}
|
||||
}
|
||||
|
||||
void server_init_xdamage()
|
||||
{
|
||||
XDamageQueryExtension(server.display, &server.xdamage_event_type, &server.xdamage_event_error_type);
|
||||
server.xdamage_event_type += XDamageNotify;
|
||||
server.xdamage_event_error_type += XDamageNotify;
|
||||
}
|
||||
|
||||
// Forward mouse click to the desktop window
|
||||
void forward_click(XEvent *e)
|
||||
{
|
||||
// forward the click to the desktop window (thanks conky)
|
||||
XUngrabPointer(server.display, e->xbutton.time);
|
||||
e->xbutton.window = server.root_win;
|
||||
// icewm doesn't open under the mouse.
|
||||
// and xfce doesn't open at all.
|
||||
e->xbutton.x = e->xbutton.x_root;
|
||||
e->xbutton.y = e->xbutton.y_root;
|
||||
// fprintf(stderr, "tint2: **** %d, %d\n", e->xbutton.x, e->xbutton.y);
|
||||
// XSetInputFocus(server.display, e->xbutton.window, RevertToParent, e->xbutton.time);
|
||||
XSendEvent(server.display, e->xbutton.window, False, ButtonPressMask, e);
|
||||
}
|
||||
|
||||
void handle_crash(const char *reason)
|
||||
{
|
||||
#ifndef DISABLE_BACKTRACE
|
||||
char path[4096];
|
||||
sprintf(path, "%s/.tint2-crash.log", get_home_dir());
|
||||
int log_fd = open(path, O_WRONLY | O_CREAT | O_TRUNC, 0600);
|
||||
log_string(log_fd, RED "tint2: crashed, reason: ");
|
||||
log_string(log_fd, reason);
|
||||
log_string(log_fd, RESET "\n");
|
||||
dump_backtrace(log_fd);
|
||||
log_string(log_fd, RED "Please create a bug report with this log output." RESET "\n");
|
||||
close(log_fd);
|
||||
#endif
|
||||
}
|
||||
|
||||
void x11_io_error(Display *display)
|
||||
{
|
||||
handle_crash("X11 I/O error");
|
||||
}
|
||||
|
||||
#ifdef HAVE_SN
|
||||
static int error_trap_depth = 0;
|
||||
|
||||
void error_trap_push(SnDisplay *display, Display *xdisplay)
|
||||
{
|
||||
++error_trap_depth;
|
||||
}
|
||||
|
||||
void error_trap_pop(SnDisplay *display, Display *xdisplay)
|
||||
{
|
||||
if (error_trap_depth == 0) {
|
||||
fprintf(stderr, "tint2: Error trap underflow!\n");
|
||||
return;
|
||||
}
|
||||
|
||||
XSync(xdisplay, False); /* get all errors out of the queue */
|
||||
--error_trap_depth;
|
||||
}
|
||||
#endif // HAVE_SN
|
||||
@@ -93,6 +93,15 @@ typedef struct Global_atom {
|
||||
Atom TARGETS;
|
||||
} Global_atom;
|
||||
|
||||
typedef struct Property {
|
||||
unsigned char *data;
|
||||
int format, nitems;
|
||||
Atom type;
|
||||
} Property;
|
||||
|
||||
// Returns the name of an Atom as string. Do not free the string.
|
||||
const char *GetAtomName(Display *disp, Atom a);
|
||||
|
||||
typedef struct Monitor {
|
||||
int x;
|
||||
int y;
|
||||
@@ -111,6 +120,7 @@ typedef struct Viewport {
|
||||
|
||||
typedef struct Server {
|
||||
Display *display;
|
||||
int x11_fd;
|
||||
Window root_win;
|
||||
Window composite_manager;
|
||||
gboolean real_transparency;
|
||||
@@ -135,6 +145,9 @@ typedef struct Server {
|
||||
Colormap colormap;
|
||||
Colormap colormap32;
|
||||
Global_atom atom;
|
||||
int xdamage_event_type;
|
||||
int xdamage_event_error_type;
|
||||
gboolean has_shm;
|
||||
#ifdef HAVE_SN
|
||||
SnDisplay *sn_display;
|
||||
GTree *pids;
|
||||
@@ -153,6 +166,10 @@ Atom server_get_atom(char *atom_name);
|
||||
void server_catch_error(Display *d, XErrorEvent *ev);
|
||||
void server_init_atoms();
|
||||
void server_init_visual();
|
||||
void server_init_xdamage();
|
||||
|
||||
void x11_io_error(Display *display);
|
||||
void handle_crash(const char *reason);
|
||||
|
||||
// detect root background
|
||||
void get_root_pixmap();
|
||||
@@ -167,4 +184,12 @@ GSList *get_desktop_names();
|
||||
int get_current_desktop();
|
||||
void change_desktop(int desktop);
|
||||
|
||||
// Forward mouse click to the desktop window
|
||||
void forward_click(XEvent *e);
|
||||
|
||||
#ifdef HAVE_SN
|
||||
void error_trap_push(SnDisplay *display, Display *xdisplay);
|
||||
void error_trap_pop(SnDisplay *display, Display *xdisplay);
|
||||
#endif
|
||||
|
||||
#endif
|
||||
170
src/util/signals.c
Normal file
170
src/util/signals.c
Normal file
@@ -0,0 +1,170 @@
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <glib.h>
|
||||
#ifndef TINT2CONF
|
||||
#ifdef HAVE_SN
|
||||
#include <libsn/sn.h>
|
||||
#endif
|
||||
#endif
|
||||
#include <signal.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <sys/wait.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "common.h"
|
||||
#include "panel.h"
|
||||
#include "launcher.h"
|
||||
#include "server.h"
|
||||
#include "signals.h"
|
||||
|
||||
static sig_atomic_t signal_pending;
|
||||
|
||||
void signal_handler(int sig)
|
||||
{
|
||||
// signal handler is light as it should be
|
||||
signal_pending = sig;
|
||||
}
|
||||
|
||||
void reset_signals()
|
||||
{
|
||||
for (int sig = 1; sig < 32; sig++) {
|
||||
signal(sig, SIG_DFL);
|
||||
}
|
||||
sigset_t signal_set;
|
||||
sigemptyset(&signal_set);
|
||||
sigprocmask(SIG_SETMASK, &signal_set, NULL);
|
||||
}
|
||||
|
||||
#ifndef TINT2CONF
|
||||
void init_signals()
|
||||
{
|
||||
// Set signal handlers
|
||||
signal_pending = 0;
|
||||
|
||||
reset_signals();
|
||||
|
||||
struct sigaction sa_chld = {.sa_handler = SIG_IGN};
|
||||
sigaction(SIGCHLD, &sa_chld, 0);
|
||||
|
||||
struct sigaction sa = {.sa_handler = signal_handler, .sa_flags = SA_RESTART};
|
||||
sigaction(SIGUSR1, &sa, 0);
|
||||
sigaction(SIGUSR2, &sa, 0);
|
||||
sigaction(SIGINT, &sa, 0);
|
||||
sigaction(SIGTERM, &sa, 0);
|
||||
sigaction(SIGHUP, &sa, 0);
|
||||
|
||||
#ifdef BACKTRACE_ON_SIGNAL
|
||||
struct sigaction sa_crash = {.sa_handler = crash_handler};
|
||||
sigaction(SIGSEGV, &sa_crash, 0);
|
||||
sigaction(SIGFPE, &sa_crash, 0);
|
||||
sigaction(SIGPIPE, &sa_crash, 0);
|
||||
sigaction(SIGBUS, &sa_crash, 0);
|
||||
sigaction(SIGABRT, &sa_crash, 0);
|
||||
sigaction(SIGSYS, &sa_crash, 0);
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifdef BACKTRACE_ON_SIGNAL
|
||||
void crash_handler(int sig)
|
||||
{
|
||||
handle_crash(signal_name(sig));
|
||||
struct sigaction sa = {.sa_handler = SIG_DFL};
|
||||
sigaction(sig, &sa, 0);
|
||||
raise(sig);
|
||||
}
|
||||
#endif
|
||||
|
||||
int sigchild_pipe_valid = FALSE;
|
||||
int sigchild_pipe[2];
|
||||
|
||||
static void sigchld_handler(int sig)
|
||||
{
|
||||
if (!sigchild_pipe_valid)
|
||||
return;
|
||||
int savedErrno = errno;
|
||||
ssize_t unused = write(sigchild_pipe[1], "x", 1);
|
||||
(void)unused;
|
||||
fsync(sigchild_pipe[1]);
|
||||
errno = savedErrno;
|
||||
}
|
||||
|
||||
void sigchld_handler_async()
|
||||
{
|
||||
// Wait for all dead processes
|
||||
pid_t pid;
|
||||
int status;
|
||||
while ((pid = waitpid(-1, &status, WNOHANG)) != -1 && pid != 0) {
|
||||
#ifdef HAVE_SN
|
||||
if (startup_notifications) {
|
||||
SnLauncherContext *ctx = (SnLauncherContext *)g_tree_lookup(server.pids, GINT_TO_POINTER(pid));
|
||||
if (ctx) {
|
||||
g_tree_remove(server.pids, GINT_TO_POINTER(pid));
|
||||
sn_launcher_context_complete(ctx);
|
||||
sn_launcher_context_unref(ctx);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
for (GList *l = panel_config.execp_list; l; l = l->next) {
|
||||
Execp *execp = (Execp *)l->data;
|
||||
if (g_tree_lookup(execp->backend->cmd_pids, GINT_TO_POINTER(pid)))
|
||||
execp_cmd_completed(execp, pid);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void handle_sigchld_events()
|
||||
{
|
||||
if (sigchild_pipe_valid) {
|
||||
char buffer[1];
|
||||
while (read(sigchild_pipe[0], buffer, sizeof(buffer)) > 0) {
|
||||
sigchld_handler_async();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void init_signals_postconfig()
|
||||
{
|
||||
gboolean need_sigchld = FALSE;
|
||||
#ifdef HAVE_SN
|
||||
// Initialize startup-notification
|
||||
if (startup_notifications) {
|
||||
server.sn_display = sn_display_new(server.display, error_trap_push, error_trap_pop);
|
||||
server.pids = g_tree_new(cmp_ptr);
|
||||
need_sigchld = TRUE;
|
||||
}
|
||||
#endif // HAVE_SN
|
||||
if (panel_config.execp_list)
|
||||
need_sigchld = TRUE;
|
||||
|
||||
if (need_sigchld) {
|
||||
// Setup a handler for child termination
|
||||
if (pipe(sigchild_pipe) != 0) {
|
||||
fprintf(stderr, "tint2: Creating pipe failed.\n");
|
||||
} else {
|
||||
fcntl(sigchild_pipe[0], F_SETFL, O_NONBLOCK | fcntl(sigchild_pipe[0], F_GETFL));
|
||||
fcntl(sigchild_pipe[1], F_SETFL, O_NONBLOCK | fcntl(sigchild_pipe[1], F_GETFL));
|
||||
sigchild_pipe_valid = 1;
|
||||
struct sigaction act = {.sa_handler = sigchld_handler, .sa_flags = SA_RESTART};
|
||||
if (sigaction(SIGCHLD, &act, 0)) {
|
||||
perror("sigaction");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void emit_self_restart(const char *reason)
|
||||
{
|
||||
fprintf(stderr,
|
||||
YELLOW "%s %d: triggering tint2 restart, reason: %s" RESET "\n",
|
||||
__FILE__,
|
||||
__LINE__,
|
||||
reason);
|
||||
signal_pending = SIGUSR1;
|
||||
}
|
||||
|
||||
int get_signal_pending()
|
||||
{
|
||||
return signal_pending;
|
||||
}
|
||||
#endif
|
||||
15
src/util/signals.h
Normal file
15
src/util/signals.h
Normal file
@@ -0,0 +1,15 @@
|
||||
#ifndef SIGNALS_H
|
||||
#define SIGNALS_H
|
||||
|
||||
void init_signals();
|
||||
void init_signals_postconfig();
|
||||
void emit_self_restart(const char *reason);
|
||||
int get_signal_pending();
|
||||
void reset_signals();
|
||||
|
||||
void handle_sigchld_events();
|
||||
|
||||
extern int sigchild_pipe_valid;
|
||||
extern int sigchild_pipe[2];
|
||||
|
||||
#endif
|
||||
60
src/util/strlcat.c
Normal file
60
src/util/strlcat.c
Normal 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
16
src/util/strlcat.h
Normal 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
183
src/util/test.c
Normal 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
72
src/util/test.h
Normal 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
|
||||
1979
src/util/timer.c
1979
src/util/timer.c
File diff suppressed because it is too large
Load Diff
@@ -21,44 +21,51 @@
|
||||
#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 debug_timers;
|
||||
|
||||
extern struct timeval next_timeout;
|
||||
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();
|
||||
|
||||
// Updates next_timeout to the value, when the next installed timeout will expire
|
||||
void update_next_timeout();
|
||||
// Initialize a timer. Caller keeps ownership.
|
||||
void init_timer(Timer *timer, const char *name);
|
||||
|
||||
// Callback of all expired timeouts
|
||||
void callback_timeout_expired();
|
||||
// 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 +76,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
|
||||
|
||||
165
src/util/tracing.c
Normal file
165
src/util/tracing.c
Normal file
@@ -0,0 +1,165 @@
|
||||
#include "timer.h"
|
||||
|
||||
#ifdef HAVE_TRACING
|
||||
|
||||
#ifdef ENABLE_EXECINFO
|
||||
#include <execinfo.h>
|
||||
#endif
|
||||
#include <glib.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.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"
|
||||
|
||||
static GList *tracing_events = NULL;
|
||||
static sig_atomic_t tracing = FALSE;
|
||||
|
||||
typedef struct TracingEvent {
|
||||
void *address;
|
||||
void *caller;
|
||||
double time;
|
||||
gboolean enter;
|
||||
} TracingEvent;
|
||||
|
||||
void __attribute__ ((constructor)) init_tracing()
|
||||
{
|
||||
tracing_events = NULL;
|
||||
tracing = FALSE;
|
||||
}
|
||||
|
||||
void cleanup_tracing()
|
||||
{
|
||||
g_list_free_full(tracing_events, free);
|
||||
tracing_events = NULL;
|
||||
tracing = FALSE;
|
||||
}
|
||||
|
||||
char *addr2name(void *func)
|
||||
{
|
||||
#ifdef ENABLE_EXECINFO
|
||||
void *array[1];
|
||||
array[0] = func;
|
||||
char **strings = backtrace_symbols(array, 1);
|
||||
char *result = strdup(strings[0] ? strings[0] : "??");
|
||||
free(strings);
|
||||
return result;
|
||||
#else
|
||||
const size_t buf_size = 32;
|
||||
char *result = (char*) calloc(buf_size, 1);
|
||||
snprintf(result, buf_size, "%p", func);
|
||||
return result;
|
||||
#endif
|
||||
}
|
||||
|
||||
void add_tracing_event(void *func, void *caller, gboolean enter)
|
||||
{
|
||||
TracingEvent *entry = (TracingEvent *)calloc(sizeof(TracingEvent), 1);
|
||||
entry->address = func;
|
||||
entry->caller = caller;
|
||||
entry->time = get_time();
|
||||
entry->enter = enter;
|
||||
tracing_events = g_list_append(tracing_events, entry);
|
||||
}
|
||||
|
||||
void start_tracing(void *root)
|
||||
{
|
||||
if (tracing_events)
|
||||
cleanup_tracing();
|
||||
add_tracing_event(root, NULL, TRUE);
|
||||
tracing = TRUE;
|
||||
}
|
||||
|
||||
void stop_tracing()
|
||||
{
|
||||
tracing = FALSE;
|
||||
}
|
||||
|
||||
void __cyg_profile_func_enter(void *func, void *caller)
|
||||
{
|
||||
if (tracing)
|
||||
add_tracing_event(func, caller, TRUE);
|
||||
}
|
||||
|
||||
void __cyg_profile_func_exit(void *func, void *caller)
|
||||
{
|
||||
if (tracing)
|
||||
add_tracing_event(func, caller, FALSE);
|
||||
}
|
||||
|
||||
void print_tracing_events()
|
||||
{
|
||||
GList *stack = NULL;
|
||||
int depth = 0;
|
||||
double now = get_time();
|
||||
for (GList *i = tracing_events; i; i = i->next) {
|
||||
TracingEvent *e = (TracingEvent *)i->data;
|
||||
if (e->enter) {
|
||||
// Push a new function on the stack
|
||||
for (int d = 0; d < depth; d++)
|
||||
fprintf(stderr, "tint2: ");
|
||||
char *name = addr2name(e->address);
|
||||
char *caller = addr2name(e->caller);
|
||||
fprintf(stderr,
|
||||
"%s called from %s\n",
|
||||
name,
|
||||
caller);
|
||||
stack = g_list_append(stack, e);
|
||||
depth++;
|
||||
} else {
|
||||
// Pop a function from the stack, if matching, and print
|
||||
if (stack) {
|
||||
TracingEvent *old = (TracingEvent *)g_list_last(stack)->data;
|
||||
if (old->address == e->address) {
|
||||
depth--;
|
||||
for (int d = 0; d < depth; d++)
|
||||
fprintf(stderr, "tint2: ");
|
||||
char *name = addr2name(e->address);
|
||||
double duration = (e->time - old->time) * 1.0e3;
|
||||
fprintf(stderr,
|
||||
"-- %s exited after %.1f ms",
|
||||
name,
|
||||
duration);
|
||||
if (duration >= 1.0) {
|
||||
fprintf(stderr, YELLOW "tint2: ");
|
||||
for (int d = 0; d < duration; d++) {
|
||||
fprintf(stderr, "tint2: #");
|
||||
}
|
||||
fprintf(stderr, RESET);
|
||||
}
|
||||
fprintf(stderr, "tint2: \n");
|
||||
free(name);
|
||||
stack = g_list_delete_link(stack, g_list_last(stack));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
while (stack) {
|
||||
TracingEvent *old = (TracingEvent *)g_list_last(stack)->data;
|
||||
depth--;
|
||||
for (int d = 0; d < depth; d++)
|
||||
fprintf(stderr, "tint2: ");
|
||||
char *name = addr2name(old->address);
|
||||
double duration = (now - old->time) * 1.0e3;
|
||||
fprintf(stderr,
|
||||
"-- %s exited after %.1f ms",
|
||||
name,
|
||||
duration);
|
||||
if (duration >= 1.0) {
|
||||
fprintf(stderr, YELLOW "tint2: ");
|
||||
for (int d = 0; d < duration; d++) {
|
||||
fprintf(stderr, "tint2: #");
|
||||
}
|
||||
fprintf(stderr, RESET);
|
||||
}
|
||||
fprintf(stderr, "tint2: \n");
|
||||
free(name);
|
||||
stack = g_list_delete_link(stack, g_list_last(stack));
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
15
src/util/tracing.h
Normal file
15
src/util/tracing.h
Normal file
@@ -0,0 +1,15 @@
|
||||
#ifndef TRACING_H
|
||||
#define TRACING_H
|
||||
|
||||
#ifdef HAVE_TRACING
|
||||
|
||||
void init_tracing();
|
||||
void cleanup_tracing();
|
||||
|
||||
void start_tracing(void *root);
|
||||
void stop_tracing();
|
||||
void print_tracing_events();
|
||||
|
||||
#endif
|
||||
|
||||
#endif
|
||||
@@ -17,6 +17,9 @@
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
**************************************************************************/
|
||||
|
||||
#include "uevent.h"
|
||||
int uevent_fd = -1;
|
||||
|
||||
#ifdef ENABLE_UEVENT
|
||||
|
||||
#include <string.h>
|
||||
@@ -30,9 +33,7 @@
|
||||
#include <linux/netlink.h>
|
||||
|
||||
#include "common.h"
|
||||
#include "uevent.h"
|
||||
|
||||
static int ueventfd = -1;
|
||||
static struct sockaddr_nl nls;
|
||||
static GList *notifiers = NULL;
|
||||
|
||||
@@ -146,11 +147,11 @@ void uevent_unregister_notifier(struct uevent_notify *nb)
|
||||
|
||||
void uevent_handler()
|
||||
{
|
||||
if (ueventfd < 0)
|
||||
if (uevent_fd < 0)
|
||||
return;
|
||||
|
||||
char buf[512];
|
||||
int len = recv(ueventfd, buf, sizeof(buf), MSG_DONTWAIT);
|
||||
int len = recv(uevent_fd, buf, sizeof(buf), MSG_DONTWAIT);
|
||||
if (len < 0)
|
||||
return;
|
||||
|
||||
@@ -181,27 +182,27 @@ int uevent_init()
|
||||
nls.nl_groups = -1;
|
||||
|
||||
/* open socket */
|
||||
ueventfd = socket(PF_NETLINK, SOCK_DGRAM, NETLINK_KOBJECT_UEVENT);
|
||||
if (ueventfd < 0) {
|
||||
fprintf(stderr, "Error: socket open failed\n");
|
||||
uevent_fd = socket(PF_NETLINK, SOCK_DGRAM, NETLINK_KOBJECT_UEVENT);
|
||||
if (uevent_fd < 0) {
|
||||
fprintf(stderr, "tint2: Error: socket open failed\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Listen to netlink socket */
|
||||
if (bind(ueventfd, (void *)&nls, sizeof(struct sockaddr_nl))) {
|
||||
fprintf(stderr, "Bind failed\n");
|
||||
if (bind(uevent_fd, (void *)&nls, sizeof(struct sockaddr_nl))) {
|
||||
fprintf(stderr, "tint2: Bind failed\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
printf("Kernel uevent interface initialized...\n");
|
||||
fprintf(stderr, "tint2: Kernel uevent interface initialized...\n");
|
||||
|
||||
return ueventfd;
|
||||
return uevent_fd;
|
||||
}
|
||||
|
||||
void uevent_cleanup()
|
||||
{
|
||||
if (ueventfd >= 0)
|
||||
close(ueventfd);
|
||||
if (uevent_fd >= 0)
|
||||
close(uevent_fd);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
@@ -20,6 +20,8 @@
|
||||
#ifndef UEVENT_H
|
||||
#define UEVENT_H
|
||||
|
||||
#include <glib.h>
|
||||
|
||||
enum uevent_action {
|
||||
UEVENT_UNKNOWN = 0x01,
|
||||
UEVENT_ADD = 0x02,
|
||||
@@ -48,6 +50,8 @@ struct uevent_notify {
|
||||
void (*cb)(struct uevent *e, void *userdata);
|
||||
};
|
||||
|
||||
extern int uevent_fd;
|
||||
|
||||
#if ENABLE_UEVENT
|
||||
int uevent_init();
|
||||
void uevent_cleanup();
|
||||
|
||||
@@ -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, "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;
|
||||
}
|
||||
@@ -185,20 +192,23 @@ int get_window_monitor(Window win)
|
||||
|
||||
if (best_match < 0)
|
||||
best_match = 0;
|
||||
// fprintf(stderr, "desktop %d, window %lx %s : monitor %d, (%d, %d)\n", 1 + get_current_desktop(), win,
|
||||
// fprintf(stderr, "tint2: desktop %d, window %lx %s : monitor %d, (%d, %d)\n", 1 + get_current_desktop(), win,
|
||||
// get_task(win) ? get_task(win)->title : "??", best_match+1, x, y);
|
||||
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,251 @@ 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++) {
|
||||
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)
|
||||
goto err0;
|
||||
|
||||
if (window_is_iconified(win))
|
||||
goto err0;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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) {
|
||||
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)
|
||||
goto err4;
|
||||
|
||||
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;
|
||||
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);
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
cairo_surface_t *get_window_thumbnail(Window win, int size)
|
||||
{
|
||||
cairo_surface_t *image_surface = NULL;
|
||||
const gboolean shm_allowed = FALSE;
|
||||
if (shm_allowed && 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;
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user