Compare commits
199
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
01f823cf79 | ||
|
|
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 |
@@ -1,3 +1,5 @@
|
|||||||
build
|
build
|
||||||
*.user
|
*.user
|
||||||
version.h
|
version.h
|
||||||
|
*.todo
|
||||||
|
*.pyc
|
||||||
|
|||||||
@@ -30,6 +30,8 @@ Contributors:
|
|||||||
Michael Messmore : Support for Path in .desktop files
|
Michael Messmore : Support for Path in .desktop files
|
||||||
Matthew Otnel : config option systray_name_filter
|
Matthew Otnel : config option systray_name_filter
|
||||||
Ryan Gray, Jeff Blake (https://gitlab.com/berkley4) : battery format
|
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:
|
Translations:
|
||||||
Bosnian:
|
Bosnian:
|
||||||
@@ -44,3 +46,5 @@ Translations:
|
|||||||
Daniel Napora <napcok@gmail.com>
|
Daniel Napora <napcok@gmail.com>
|
||||||
Serbian:
|
Serbian:
|
||||||
Dino Duratović <dinomol@mail.com>
|
Dino Duratović <dinomol@mail.com>
|
||||||
|
Spanish:
|
||||||
|
Vic <vicmz@yandex.com>
|
||||||
|
|||||||
+35
-11
@@ -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_EXTRA_THEMES "Install additional tint2 themes" ON )
|
||||||
option( ENABLE_RSVG "Rsvg support (launcher only)" ON )
|
option( ENABLE_RSVG "Rsvg support (launcher only)" ON )
|
||||||
option( ENABLE_SN "Startup notification support" 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_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 "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 )
|
option( ENABLE_BACKTRACE_ON_SIGNAL "Dump a backtrace also when receiving signals such as SIGSEGV" OFF )
|
||||||
@@ -24,7 +25,7 @@ endif()
|
|||||||
include( FindPkgConfig )
|
include( FindPkgConfig )
|
||||||
include( CheckLibraryExists )
|
include( CheckLibraryExists )
|
||||||
include( CheckCSourceCompiles )
|
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( PANGOCAIRO REQUIRED pangocairo )
|
||||||
pkg_check_modules( PANGO REQUIRED pango )
|
pkg_check_modules( PANGO REQUIRED pango )
|
||||||
pkg_check_modules( CAIRO REQUIRED cairo )
|
pkg_check_modules( CAIRO REQUIRED cairo )
|
||||||
@@ -83,10 +84,10 @@ if( NOT IMLIB_BUILD_WITH_X )
|
|||||||
endif( 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")
|
if(CMAKE_SYSTEM_NAME STREQUAL "Linux")
|
||||||
add_definitions( -D_POSIX_C_SOURCE=200809L -D_BSD_SOURCE -D_DEFAULT_SOURCE )
|
add_definitions( -D_POSIX_C_SOURCE=200809L )
|
||||||
else(CMAKE_SYSTEM_NAME STREQUAL "Linux")
|
|
||||||
add_definitions( -D_WITH_GETLINE )
|
|
||||||
endif(CMAKE_SYSTEM_NAME STREQUAL "Linux")
|
endif(CMAKE_SYSTEM_NAME STREQUAL "Linux")
|
||||||
|
|
||||||
|
|
||||||
@@ -116,7 +117,12 @@ include_directories( ${PROJECT_BINARY_DIR}
|
|||||||
set( SOURCES src/config.c
|
set( SOURCES src/config.c
|
||||||
src/panel.c
|
src/panel.c
|
||||||
src/server.c
|
src/server.c
|
||||||
src/tint.c
|
src/main.c
|
||||||
|
src/init.c
|
||||||
|
src/signals.c
|
||||||
|
src/tracing.c
|
||||||
|
src/mouse_actions.c
|
||||||
|
src/drag_and_drop.c
|
||||||
src/clock/clock.c
|
src/clock/clock.c
|
||||||
src/systray/systraybar.c
|
src/systray/systraybar.c
|
||||||
src/launcher/launcher.c
|
src/launcher/launcher.c
|
||||||
@@ -135,11 +141,16 @@ set( SOURCES src/config.c
|
|||||||
src/tint2rc.c
|
src/tint2rc.c
|
||||||
src/util/area.c
|
src/util/area.c
|
||||||
src/util/common.c
|
src/util/common.c
|
||||||
|
src/util/fps_distribution.c
|
||||||
src/util/strnatcmp.c
|
src/util/strnatcmp.c
|
||||||
src/util/timer.c
|
src/util/timer.c
|
||||||
src/util/cache.c
|
src/util/cache.c
|
||||||
src/util/color.c
|
src/util/color.c
|
||||||
src/util/gradient.c
|
src/util/gradient.c
|
||||||
|
src/util/addr2line.c
|
||||||
|
src/util/print.c
|
||||||
|
src/util/mem.c
|
||||||
|
src/util/uevent.c
|
||||||
src/util/window.c )
|
src/util/window.c )
|
||||||
|
|
||||||
if( ENABLE_BATTERY )
|
if( ENABLE_BATTERY )
|
||||||
@@ -180,7 +191,6 @@ endif( ENABLE_SN)
|
|||||||
|
|
||||||
if( ENABLE_UEVENT )
|
if( ENABLE_UEVENT )
|
||||||
add_definitions( -DENABLE_UEVENT )
|
add_definitions( -DENABLE_UEVENT )
|
||||||
set( SOURCES ${SOURCES} src/util/uevent.c)
|
|
||||||
endif( ENABLE_UEVENT )
|
endif( ENABLE_UEVENT )
|
||||||
|
|
||||||
if(ENABLE_BACKTRACE)
|
if(ENABLE_BACKTRACE)
|
||||||
@@ -210,13 +220,22 @@ if( ENABLE_TINT2CONF )
|
|||||||
endif( ENABLE_TINT2CONF )
|
endif( ENABLE_TINT2CONF )
|
||||||
|
|
||||||
if( ENABLE_ASAN )
|
if( ENABLE_ASAN )
|
||||||
SET(ASAN_C_FLAGS " -O0 -g3 -gdwarf-2 -fsanitize=address -fno-common -fno-omit-frame-pointer -rdynamic -Wshadow")
|
SET(ASAN_C_FLAGS " -O0 -g3 -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_L_FLAGS " -O0 -g3 -fsanitize=address -fno-common -fno-omit-frame-pointer -rdynamic -fuse-ld=gold ")
|
||||||
else()
|
else()
|
||||||
SET(ASAN_C_FLAGS "")
|
SET(ASAN_C_FLAGS "")
|
||||||
SET(ASAN_L_FLAGS "")
|
SET(ASAN_L_FLAGS "")
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
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" -- "\"${PROJECT_SOURCE_DIR}/\"" )
|
add_custom_target( version ALL "${PROJECT_SOURCE_DIR}/get_version.sh" -- "\"${PROJECT_SOURCE_DIR}/\"" )
|
||||||
|
|
||||||
link_directories( ${X11_LIBRARY_DIRS}
|
link_directories( ${X11_LIBRARY_DIRS}
|
||||||
@@ -249,15 +268,20 @@ if( RT_LIBRARY )
|
|||||||
endif( RT_LIBRARY )
|
endif( RT_LIBRARY )
|
||||||
|
|
||||||
target_link_libraries( tint2 m )
|
target_link_libraries( tint2 m )
|
||||||
|
if(ENABLE_BACKTRACE)
|
||||||
|
target_link_libraries( tint2 dl )
|
||||||
|
target_link_libraries( tint2 z )
|
||||||
|
target_link_libraries( tint2 bfd )
|
||||||
|
endif(ENABLE_BACKTRACE)
|
||||||
|
|
||||||
add_dependencies( tint2 version )
|
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 COMPILE_FLAGS "-Wall -Wpointer-arith -fno-strict-aliasing -pthread -std=c11 ${ASAN_C_FLAGS} ${TRACING_C_FLAGS}" )
|
||||||
set_target_properties( tint2 PROPERTIES LINK_FLAGS "-pthread -fno-strict-aliasing ${ASAN_L_FLAGS} ${BACKTRACE_L_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( TARGETS tint2 DESTINATION bin )
|
||||||
install( FILES tint2.svg DESTINATION ${CMAKE_INSTALL_DATADIR}/icons/hicolor/scalable/apps )
|
install( FILES tint2.svg DESTINATION ${CMAKE_INSTALL_DATADIR}/icons/hicolor/scalable/apps )
|
||||||
install( FILES tint2.desktop DESTINATION ${CMAKE_INSTALL_DATADIR}/applications )
|
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 default_icon.png DESTINATION ${CMAKE_INSTALL_DATADIR}/tint2 )
|
||||||
install( FILES AUTHORS ChangeLog README.md doc/tint2.md DESTINATION ${docdir} )
|
install( FILES AUTHORS ChangeLog README.md doc/tint2.md DESTINATION ${docdir} )
|
||||||
install( FILES doc/manual.html doc/readme.html DESTINATION ${htmldir} )
|
install( FILES doc/manual.html doc/readme.html DESTINATION ${htmldir} )
|
||||||
|
|||||||
@@ -1,7 +1,36 @@
|
|||||||
2017-06-27 master
|
2017-11-10 master
|
||||||
|
- Enhancements:
|
||||||
|
- Added Spanish translation (contributed by Vicmz)
|
||||||
|
- Executor: updated tooltip documentation (issue #676)
|
||||||
|
- Systray: warn on duplicate config option systray_name_filter (issue #652)
|
||||||
|
|
||||||
|
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:
|
- Fixes:
|
||||||
- Clock, executors and other timers fire correctly after waking up from suspend
|
- 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:
|
- 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
|
- Taskbar: new config option taskbar_hide_different_desktop
|
||||||
- Battery:
|
- Battery:
|
||||||
- New config option bat1_format and bat2_format
|
- New config option bat1_format and bat2_format
|
||||||
@@ -12,8 +41,27 @@
|
|||||||
- If no user tooltip is set, displays the script standard error as tooltip.
|
- 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").
|
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.
|
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:
|
- Configuration changes:
|
||||||
- Removed primary_monitor_first as it was conflicting with taskbar behavior; use *_monitor = primary instead.
|
- 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
|
2017-06-11 0.14.6
|
||||||
- Fixes:
|
- Fixes:
|
||||||
@@ -919,3 +967,7 @@ released tint-0.2
|
|||||||
2008-04-22
|
2008-04-22
|
||||||
- fork ttm projet from p://code.google.com/p/ttm/ (by Pål Staurland staura@gmail.com)
|
- 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'.
|
while the projet is no longer in developpement, have not changed the name of 'tint'.
|
||||||
|
.
|
||||||
|
.
|
||||||
|
.
|
||||||
|
.
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
# Latest stable release: 0.14.6
|
# Latest stable release: 15.3
|
||||||
Changes: https://gitlab.com/o9000/tint2/blob/0.14.6/ChangeLog
|
Changes: https://gitlab.com/o9000/tint2/blob/15.3/ChangeLog
|
||||||
|
|
||||||
Documentation: [doc/tint2.md](doc/tint2.md)
|
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
|
git clone https://gitlab.com/o9000/tint2.git
|
||||||
cd tint2
|
cd tint2
|
||||||
git checkout 0.14.6
|
git checkout 15.3
|
||||||
mkdir build
|
mkdir build
|
||||||
cd build
|
cd build
|
||||||
cmake ..
|
cmake ..
|
||||||
@@ -85,7 +85,7 @@ tint2 is a simple panel/taskbar made for modern X window managers. It was specif
|
|||||||
|
|
||||||
## Various configs:
|
## Various configs:
|
||||||
|
|
||||||

|
* [Screenshots](https://gitlab.com/o9000/tint2/wikis/screenshots)
|
||||||
|
|
||||||
## Demos
|
## 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)
|
* [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)
|
* [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)
|
* [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)
|
||||||
|
|||||||
+7
-2
@@ -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>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>
|
<li><code>opacity</code> varies from (0 to 100), where 0 is fully transparent, 100 is fully opaque</li>
|
||||||
</ul></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>
|
</ul>
|
||||||
<p>You can define as many backgrounds as you want. For example, the following config defines two backgrounds:</p><pre class="highlight plaintext"><code>rounded = 1
|
<p>You can define as many backgrounds as you want. For example, the following config defines two backgrounds:</p><pre class="highlight plaintext"><code>rounded = 1
|
||||||
border_width = 0
|
border_width = 0
|
||||||
@@ -481,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_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_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_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>
|
<li><p><code>task_maximum_size = width height</code></p>
|
||||||
<ul>
|
<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>
|
<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>
|
||||||
@@ -601,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_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_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_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 = [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_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>
|
<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>
|
||||||
@@ -694,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 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>
|
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
|
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>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
+12
-3
@@ -199,9 +199,9 @@ pre {
|
|||||||
</style>
|
</style>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<h1 id="latest-stable-release-0-14-6"><span class="md2man-title">Latest</span> <span class="md2man-section">stable</span> <span class="md2man-date">release:</span> <span class="md2man-source">0.14.6</span><a name="latest-stable-release-0-14-6" href="#latest-stable-release-0-14-6" class="md2man-permalink" title="permalink"></a></h1><p>Changes: <a href="https://gitlab.com/o9000/tint2/blob/0.14.6/ChangeLog">https://gitlab.com/o9000/tint2/blob/0.14.6/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-15-3"><span class="md2man-title">Latest</span> <span class="md2man-section">stable</span> <span class="md2man-date">release:</span> <span class="md2man-source">15.3</span><a name="latest-stable-release-15-3" href="#latest-stable-release-15-3" class="md2man-permalink" title="permalink"></a></h1><p>Changes: <a href="https://gitlab.com/o9000/tint2/blob/15.3/ChangeLog">https://gitlab.com/o9000/tint2/blob/15.3/ChangeLog</a></p><p>Documentation: <a href="manual.html">manual.html</a></p><p>Compile it with (after you install the <a href="https://gitlab.com/o9000/tint2/wikis/Install#dependencies">dependencies</a>):</p><pre class="highlight plaintext"><code>git clone https://gitlab.com/o9000/tint2.git
|
||||||
cd tint2
|
cd tint2
|
||||||
git checkout 0.14.6
|
git checkout 15.3
|
||||||
mkdir build
|
mkdir build
|
||||||
cd build
|
cd build
|
||||||
cmake ..
|
cmake ..
|
||||||
@@ -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>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>
|
<li>Old project location (inactive): <a href="https://code.google.com/p/tint2">https://code.google.com/p/tint2</a></li>
|
||||||
</ul>
|
</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>
|
<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.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.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.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>
|
<li><a href="https://gitlab.com/o9000/tint2/wikis/whats-new-0.12.gif">Distribute size between taskbars, freespace</a></li>
|
||||||
</ul>
|
</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>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
+11
-2
@@ -1,4 +1,4 @@
|
|||||||
.TH TINT2 1 "2017\-06\-11" 0.14.6
|
.TH TINT2 1 "2017\-11\-05" 15.3
|
||||||
.SH NAME
|
.SH NAME
|
||||||
.PP
|
.PP
|
||||||
tint2 \- lightweight panel/taskbar
|
tint2 \- lightweight panel/taskbar
|
||||||
@@ -150,6 +150,10 @@ The tint2 config file starts with the options defining background elements with
|
|||||||
.IP \(bu 2
|
.IP \(bu 2
|
||||||
\fB\fCopacity\fR varies from (0 to 100), where 0 is fully transparent, 100 is fully opaque
|
\fB\fCopacity\fR varies from (0 to 100), where 0 is fully transparent, 100 is fully opaque
|
||||||
.RE
|
.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
|
.RE
|
||||||
.PP
|
.PP
|
||||||
You can define as many backgrounds as you want. For example, the following config defines two backgrounds:
|
You can define as many backgrounds as you want. For example, the following config defines two backgrounds:
|
||||||
@@ -508,6 +512,10 @@ The following options configure the task buttons in the taskbar:
|
|||||||
.IP \(bu 2
|
.IP \(bu 2
|
||||||
\fB\fCtask_tooltip = boolean (0 or 1)\fR : Whether to show tooltips for tasks.
|
\fB\fCtask_tooltip = boolean (0 or 1)\fR : Whether to show tooltips for tasks.
|
||||||
.IP \(bu 2
|
.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
|
\fB\fCtask_maximum_size = width height\fR
|
||||||
.RS
|
.RS
|
||||||
.IP \(bu 2
|
.IP \(bu 2
|
||||||
@@ -716,7 +724,7 @@ To hide the clock, comment \fB\fCtime1_format\fR and \fB\fCtime2_format\fR\&.
|
|||||||
.IP \(bu 2
|
.IP \(bu 2
|
||||||
\fB\fCexecp_icon_h = integer\fR : See \fB\fCexecp_icon_w\fR\&. \fI(since 0.12.4)\fP
|
\fB\fCexecp_icon_h = integer\fR : See \fB\fCexecp_icon_w\fR\&. \fI(since 0.12.4)\fP
|
||||||
.IP \(bu 2
|
.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
|
.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
|
\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
|
.IP \(bu 2
|
||||||
@@ -895,3 +903,4 @@ and the wiki page at \[la]https://gitlab.com/o9000/tint2/wikis/home\[ra]\&.
|
|||||||
.PP
|
.PP
|
||||||
This documentation is also provided in HTML and Markdown format in the system's default location
|
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\&.
|
for documentation files, usually \fB\fC/usr/share/doc/tint2\fR or \fB\fC/usr/local/share/doc/tint2\fR\&.
|
||||||
|
\&.
|
||||||
|
|||||||
+11
-2
@@ -1,4 +1,4 @@
|
|||||||
# TINT2 1 "2017-06-11" 0.14.6
|
# TINT2 1 "2017-11-05" 15.3
|
||||||
|
|
||||||
## NAME
|
## NAME
|
||||||
tint2 - lightweight panel/taskbar
|
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
|
* `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
|
* `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:
|
You can define as many backgrounds as you want. For example, the following config defines two backgrounds:
|
||||||
|
|
||||||
```
|
```
|
||||||
@@ -409,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_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`
|
* `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.
|
* `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.
|
* `height` is used with vertical panels.
|
||||||
@@ -591,7 +599,7 @@ The action semantics:
|
|||||||
|
|
||||||
* `execp_icon_h = integer` : See `execp_icon_w`. *(since 0.12.4)*
|
* `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)*
|
* `execp_font = [FAMILY-LIST] [STYLE-OPTIONS] [SIZE]` : The font used to draw the text. *(since 0.12.4)*
|
||||||
|
|
||||||
@@ -752,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
|
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`.
|
for documentation files, usually `/usr/share/doc/tint2` or `/usr/local/share/doc/tint2`.
|
||||||
|
.
|
||||||
|
|||||||
+20
-6
@@ -1,9 +1,10 @@
|
|||||||
#!/bin/sh
|
#!/bin/sh
|
||||||
|
|
||||||
MAJOR=0.14
|
SCRIPT_DIR=$(dirname "$0")
|
||||||
DIRTY=""
|
DIRTY=""
|
||||||
|
VERSION=""
|
||||||
|
|
||||||
if git status 1>/dev/null 2>/dev/null
|
if [ -d ${SCRIPT_DIR}/.git ] && git status 1>/dev/null 2>/dev/null
|
||||||
then
|
then
|
||||||
git update-index -q --ignore-submodules --refresh
|
git update-index -q --ignore-submodules --refresh
|
||||||
# Disallow unstaged changes in the working tree
|
# Disallow unstaged changes in the working tree
|
||||||
@@ -31,9 +32,23 @@ then
|
|||||||
DIRTY="-dirty"
|
DIRTY="-dirty"
|
||||||
fi
|
fi
|
||||||
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
|
if git describe 1>/dev/null 2>/dev/null
|
||||||
else
|
then
|
||||||
SCRIPT_DIR=$(dirname "$0")
|
VERSION=$(git describe 2>/dev/null)$DIRTY
|
||||||
|
elif git log -n 1 1>/dev/null 2>/dev/null
|
||||||
|
then
|
||||||
|
VERSION=$(head -n 1 "${SCRIPT_DIR}/ChangeLog" | cut -d ' ' -f 2)
|
||||||
|
if [ "$VERSION" = "master" ]
|
||||||
|
then
|
||||||
|
PREVIOUS=$(grep '^2' "${SCRIPT_DIR}/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 "${SCRIPT_DIR}/ChangeLog" | cut -d ' ' -f 2)
|
VERSION=$(head -n 1 "${SCRIPT_DIR}/ChangeLog" | cut -d ' ' -f 2)
|
||||||
if [ "$VERSION" = "master" ]
|
if [ "$VERSION" = "master" ]
|
||||||
then
|
then
|
||||||
@@ -41,7 +56,6 @@ else
|
|||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
|
||||||
VERSION=$(echo "$VERSION" | sed 's/^v//')
|
VERSION=$(echo "$VERSION" | sed 's/^v//')
|
||||||
|
|
||||||
echo '#define VERSION_STRING "'$VERSION'"' > version.h
|
echo '#define VERSION_STRING "'$VERSION'"' > version.h
|
||||||
|
|||||||
+37
-81
@@ -76,37 +76,21 @@ def get_last_version():
|
|||||||
return tags[-1]
|
return tags[-1]
|
||||||
|
|
||||||
|
|
||||||
def inc_version(v, major=False, minor=False, rc=False):
|
def inc_version(v, feature=False):
|
||||||
if "-rc" in v:
|
if v.startswith("v0."):
|
||||||
# v4.0-rc7 -> v4.0-rc8 or v4.0
|
assert v == "v0.14.6"
|
||||||
if minor:
|
return "v15.0"
|
||||||
return v.split("-rc")[0]
|
# v4.11 -> v4.12 or v5.0
|
||||||
else:
|
parts = v.split(".")
|
||||||
parts = v.split("-rc")
|
while len(parts) < 2:
|
||||||
parts[-1] = str(int(parts[-1]) + 1)
|
parts.append("0")
|
||||||
return "-rc".join(parts)
|
assert len(parts) == 2
|
||||||
|
if feature:
|
||||||
|
parts[-2] = "v" + str(int(parts[-2].replace("v", "")) + 1)
|
||||||
|
parts[-1] = "0"
|
||||||
else:
|
else:
|
||||||
# v4.11 = v4, 11, 0 -> v4.11.1 or v4.12 or v.4.12-rc1 or v5.0 or v5.0-rc1
|
parts[-1] = str(int(parts[-1]) + 1)
|
||||||
# v4.11.7 = v4, 11, 7 -> ...
|
return ".".join([s for s in parts if s])
|
||||||
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])
|
|
||||||
|
|
||||||
|
|
||||||
def assert_equal(a, b):
|
def assert_equal(a, b):
|
||||||
@@ -117,53 +101,23 @@ def assert_equal(a, b):
|
|||||||
|
|
||||||
def test_inc_version():
|
def test_inc_version():
|
||||||
# auto
|
# auto
|
||||||
assert_equal(inc_version("v1.0"), "v1.0.1")
|
assert_equal(inc_version("v0.14.6"), "v15.0")
|
||||||
assert_equal(inc_version("v1.0.1"), "v1.0.2")
|
assert_equal(inc_version("v15"), "v15.1")
|
||||||
assert_equal(inc_version("v1.0.2"), "v1.0.3")
|
assert_equal(inc_version("v15.0"), "v15.1")
|
||||||
assert_equal(inc_version("v1.0.10"), "v1.0.11")
|
assert_equal(inc_version("v16.1"), "v16.2")
|
||||||
assert_equal(inc_version("v1.1.10"), "v1.1.11")
|
assert_equal(inc_version("v16.10"), "v16.11")
|
||||||
assert_equal(inc_version("v1.1.10"), "v1.1.11")
|
# fix
|
||||||
# rc
|
assert_equal(inc_version("v0.14.6", False), "v15.0")
|
||||||
assert_equal(inc_version("v1.0", False, False, True), "v1.1-rc1")
|
assert_equal(inc_version("v15", False), "v15.1")
|
||||||
assert_equal(inc_version("v1.0.1", False, False, True), "v1.1-rc1")
|
assert_equal(inc_version("v15.0", False), "v15.1")
|
||||||
assert_equal(inc_version("v1.0.2", False, False, True), "v1.1-rc1")
|
assert_equal(inc_version("v16.1", False), "v16.2")
|
||||||
assert_equal(inc_version("v1.0.10", False, False, True), "v1.1-rc1")
|
assert_equal(inc_version("v16.10", False), "v16.11")
|
||||||
assert_equal(inc_version("v1.1.10", False, False, True), "v1.2-rc1")
|
# feature
|
||||||
assert_equal(inc_version("v1.1.10", False, False, True), "v1.2-rc1")
|
assert_equal(inc_version("v15", True), "v16.0")
|
||||||
# minor
|
assert_equal(inc_version("v15.0", True), "v16.0")
|
||||||
assert_equal(inc_version("v1.0", False, True, False), "v1.1")
|
assert_equal(inc_version("v15.1", True), "v16.0")
|
||||||
assert_equal(inc_version("v1.0.1", False, True, False), "v1.1")
|
assert_equal(inc_version("v15.2", True), "v16.0")
|
||||||
assert_equal(inc_version("v1.0.2", False, True, False), "v1.1")
|
assert_equal(inc_version("v15.10", True), "v16.0")
|
||||||
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")
|
|
||||||
|
|
||||||
|
|
||||||
def replace_in_file(path, before, after):
|
def replace_in_file(path, before, after):
|
||||||
@@ -198,9 +152,7 @@ def update_log(path, version, date):
|
|||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
parser = argparse.ArgumentParser()
|
parser = argparse.ArgumentParser()
|
||||||
parser.add_argument("--major", action="store_true")
|
parser.add_argument("--feature", action="store_true")
|
||||||
parser.add_argument("--minor", action="store_true")
|
|
||||||
parser.add_argument("--rc", action="store_true")
|
|
||||||
parser.add_argument("--undo", action="store_true")
|
parser.add_argument("--undo", action="store_true")
|
||||||
args = parser.parse_args()
|
args = parser.parse_args()
|
||||||
logging.basicConfig(format=ansi_lblue + "%(asctime)s %(pathname)s %(levelname)s" + ansi_reset + " %(message)s", level=logging.DEBUG)
|
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")
|
os.system("git log -1")
|
||||||
sys.exit(0)
|
sys.exit(0)
|
||||||
info("Old version:", old_version)
|
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", "")
|
readable_version = version.replace("v", "")
|
||||||
date = datetime.datetime.now().strftime("%Y-%m-%d")
|
date = datetime.datetime.now().strftime("%Y-%m-%d")
|
||||||
info("New version:", readable_version, version, date)
|
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)
|
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)
|
assert_equal(run("./tint2-%s/build/tint2 -v" % readable_version).strip(), "tint2 version %s" % readable_version)
|
||||||
os.system("git log -p -1 --word-diff")
|
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))
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
dh $@
|
dh $@
|
||||||
|
|
||||||
override_dh_auto_configure:
|
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:
|
override_dh_auto_install:
|
||||||
dh_auto_install --destdir=$(CURDIR)/debian/tmp
|
dh_auto_install --destdir=$(CURDIR)/debian/tmp
|
||||||
|
|||||||
@@ -49,7 +49,7 @@ echo "echo \"#define VERSION_STRING \\\"$VERSION\\\"\" > version.h" > $DIR/get_v
|
|||||||
# Copy the debian files into the source directory
|
# Copy the debian files into the source directory
|
||||||
cp -r debian $DIR/debian
|
cp -r debian $DIR/debian
|
||||||
|
|
||||||
for DISTRO in precise trusty xenial yakkety zesty artful
|
for DISTRO in trusty xenial zesty artful
|
||||||
do
|
do
|
||||||
# Cleanup from previous builds
|
# Cleanup from previous builds
|
||||||
rm -rf tint2_$VERSION-*
|
rm -rf tint2_$VERSION-*
|
||||||
|
|||||||
Executable
+23
@@ -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
|
||||||
Executable
+422
@@ -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?rev=1.5&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()
|
||||||
Executable
+208
@@ -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()
|
||||||
@@ -197,7 +197,7 @@ void battery_update_text(char *dest, char *format)
|
|||||||
if (battery_state.state == BATTERY_FULL) {
|
if (battery_state.state == BATTERY_FULL) {
|
||||||
snprintf(buf, sizeof(buf), "Full");
|
snprintf(buf, sizeof(buf), "Full");
|
||||||
strnappend(dest, buf, BATTERY_BUF_SIZE);
|
strnappend(dest, buf, BATTERY_BUF_SIZE);
|
||||||
} else {
|
} 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);
|
snprintf(buf, sizeof(buf), "%02d:%02d", battery_state.time.hours, battery_state.time.minutes);
|
||||||
strnappend(dest, buf, BATTERY_BUF_SIZE);
|
strnappend(dest, buf, BATTERY_BUF_SIZE);
|
||||||
}
|
}
|
||||||
@@ -208,7 +208,7 @@ void battery_update_text(char *dest, char *format)
|
|||||||
strnappend(dest, "%", BATTERY_BUF_SIZE);
|
strnappend(dest, "%", BATTERY_BUF_SIZE);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
fprintf(stderr, "Battery: unrecognised format specifier '%%%c'.\n", *c);
|
fprintf(stderr, "tint2: Battery: unrecognised format specifier '%%%c'.\n", *c);
|
||||||
buf[0] = *c;
|
buf[0] = *c;
|
||||||
strnappend(dest, buf, BATTERY_BUF_SIZE);
|
strnappend(dest, buf, BATTERY_BUF_SIZE);
|
||||||
}
|
}
|
||||||
@@ -226,8 +226,7 @@ void init_battery()
|
|||||||
|
|
||||||
battery_found = battery_os_init();
|
battery_found = battery_os_init();
|
||||||
|
|
||||||
if (!battery_timeout)
|
battery_timeout = add_timeout(10, 30000, update_battery_tick, 0, &battery_timeout);
|
||||||
battery_timeout = add_timeout(10, 30000, update_battery_tick, 0, &battery_timeout);
|
|
||||||
|
|
||||||
update_battery();
|
update_battery();
|
||||||
}
|
}
|
||||||
@@ -436,8 +435,8 @@ void draw_battery(void *obj, cairo_t *c)
|
|||||||
void battery_dump_geometry(void *obj, int indent)
|
void battery_dump_geometry(void *obj, int indent)
|
||||||
{
|
{
|
||||||
Battery *battery = (Battery *)obj;
|
Battery *battery = (Battery *)obj;
|
||||||
fprintf(stderr, "%*sText 1: y = %d, text = %s\n", indent, "", battery->bat1_posy, buf_bat_line1);
|
fprintf(stderr, "tint2: %*sText 1: y = %d, text = %s\n", indent, "", battery->bat1_posy, buf_bat_line1);
|
||||||
fprintf(stderr, "%*sText 2: y = %d, text = %s\n", indent, "", battery->bat2_posy, buf_bat_line2);
|
fprintf(stderr, "tint2: %*sText 2: y = %d, text = %s\n", indent, "", battery->bat2_posy, buf_bat_line2);
|
||||||
}
|
}
|
||||||
|
|
||||||
char *battery_get_tooltip(void *obj)
|
char *battery_get_tooltip(void *obj)
|
||||||
@@ -465,5 +464,5 @@ void battery_action(void *obj, int button, int x, int y, Time time)
|
|||||||
command = battery_dwheel_command;
|
command = battery_dwheel_command;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
tint_exec(command, NULL, NULL, time, obj, x, y);
|
tint_exec(command, NULL, NULL, time, obj, x, y, FALSE, TRUE);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -58,7 +58,7 @@ int battery_os_update(BatteryState *state)
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
fprintf(stderr, "power update: no such sysctl");
|
fprintf(stderr, "tint2: power update: no such sysctl");
|
||||||
err = -1;
|
err = -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+42
-26
@@ -60,6 +60,20 @@ struct psy_mains {
|
|||||||
gboolean online;
|
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()
|
static void uevent_battery_update()
|
||||||
{
|
{
|
||||||
update_battery_tick(NULL);
|
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()
|
static void uevent_battery_plug()
|
||||||
{
|
{
|
||||||
printf("reinitialize batteries after HW change\n");
|
fprintf(stderr, "tint2: reinitialize batteries after HW change\n");
|
||||||
reinit_battery();
|
reinit_battery();
|
||||||
}
|
}
|
||||||
static struct uevent_notify psy_plug = {UEVENT_ADD | UEVENT_REMOVE, "power_supply", NULL, uevent_battery_plug};
|
static struct uevent_notify psy_plug = {UEVENT_ADD | UEVENT_REMOVE, "power_supply", NULL, uevent_battery_plug};
|
||||||
|
|
||||||
#define RETURN_ON_ERROR(err) \
|
#define RETURN_ON_ERROR(err) \
|
||||||
if (err) { \
|
if (err) { \
|
||||||
g_error_free(err); \
|
g_error_free(err); \
|
||||||
fprintf(stderr, RED "%s:%d: errror" RESET "\n", __FILE__, __LINE__); \
|
fprintf(stderr, RED "tint2: %s:%d: errror" RESET "\n", __FILE__, __LINE__); \
|
||||||
return FALSE; \
|
return FALSE; \
|
||||||
}
|
}
|
||||||
|
|
||||||
static GList *batteries = NULL;
|
static GList *batteries = NULL;
|
||||||
@@ -96,12 +110,13 @@ static enum psy_type power_supply_get_type(const gchar *entryname)
|
|||||||
gsize typelen;
|
gsize typelen;
|
||||||
|
|
||||||
g_file_get_contents(path_type, &type, &typelen, &error);
|
g_file_get_contents(path_type, &type, &typelen, &error);
|
||||||
g_free(path_type);
|
|
||||||
if (error) {
|
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);
|
g_error_free(error);
|
||||||
return PSY_UNKNOWN;
|
return PSY_UNKNOWN;
|
||||||
}
|
}
|
||||||
|
g_free(path_type);
|
||||||
|
|
||||||
if (!g_strcmp0(type, "Battery\n")) {
|
if (!g_strcmp0(type, "Battery\n")) {
|
||||||
g_free(type);
|
g_free(type);
|
||||||
@@ -123,8 +138,8 @@ static gboolean init_linux_battery(struct psy_battery *bat)
|
|||||||
const gchar *entryname = bat->name;
|
const gchar *entryname = bat->name;
|
||||||
|
|
||||||
bat->path_present = g_build_filename(battery_sys_prefix, "/sys/class/power_supply", entryname, "present", NULL);
|
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)) {
|
if (!is_file_non_empty(bat->path_present)) {
|
||||||
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__, bat->path_present);
|
||||||
goto err0;
|
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->path_rate_now = g_build_filename(battery_sys_prefix, "/sys/class/power_supply", entryname, "power_now", NULL);
|
||||||
bat->unit = 'W';
|
bat->unit = 'W';
|
||||||
|
|
||||||
if (!g_file_test(bat->path_level_now, G_FILE_TEST_EXISTS) ||
|
if (!is_file_non_empty(bat->path_level_now) ||
|
||||||
!g_file_test(bat->path_level_full, G_FILE_TEST_EXISTS) ||
|
!is_file_non_empty(bat->path_level_full)) {
|
||||||
!g_file_test(bat->path_rate_now, G_FILE_TEST_EXISTS)) {
|
|
||||||
g_free(bat->path_level_now);
|
g_free(bat->path_level_now);
|
||||||
g_free(bat->path_level_full);
|
g_free(bat->path_level_full);
|
||||||
g_free(bat->path_rate_now);
|
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);
|
g_build_filename(battery_sys_prefix, "/sys/class/power_supply", entryname, "current_now", NULL);
|
||||||
bat->unit = 'A';
|
bat->unit = 'A';
|
||||||
}
|
}
|
||||||
if (!g_file_test(bat->path_level_now, G_FILE_TEST_EXISTS) ||
|
if (!is_file_non_empty(bat->path_level_now)) {
|
||||||
!g_file_test(bat->path_level_full, G_FILE_TEST_EXISTS) ||
|
fprintf(stderr, RED "tint2: %s:%d: read failed for %s" RESET "\n", __FILE__, __LINE__, bat->path_level_now);
|
||||||
!g_file_test(bat->path_rate_now, G_FILE_TEST_EXISTS)) {
|
goto err1;
|
||||||
fprintf(stderr, RED "%s:%d: read failed" RESET "\n", __FILE__, __LINE__);
|
}
|
||||||
|
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;
|
goto err1;
|
||||||
}
|
}
|
||||||
|
|
||||||
bat->path_status = g_build_filename(battery_sys_prefix, "/sys/class/power_supply", entryname, "status", NULL);
|
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)) {
|
if (!is_file_non_empty(bat->path_status)) {
|
||||||
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__, bat->path_status);
|
||||||
goto err2;
|
goto err2;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -181,8 +197,8 @@ static gboolean init_linux_mains(struct psy_mains *ac)
|
|||||||
const gchar *entryname = ac->name;
|
const gchar *entryname = ac->name;
|
||||||
|
|
||||||
ac->path_online = g_build_filename(battery_sys_prefix, "/sys/class/power_supply", entryname, "online", NULL);
|
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)) {
|
if (!is_file_non_empty(ac->path_online)) {
|
||||||
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__, ac->path_online);
|
||||||
g_free(ac->path_online);
|
g_free(ac->path_online);
|
||||||
return FALSE;
|
return FALSE;
|
||||||
}
|
}
|
||||||
@@ -228,10 +244,10 @@ static void add_battery(const char *entryname)
|
|||||||
|
|
||||||
if (init_linux_battery(bat)) {
|
if (init_linux_battery(bat)) {
|
||||||
batteries = g_list_append(batteries, 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 {
|
} else {
|
||||||
g_free(bat);
|
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)) {
|
if (init_linux_mains(ac)) {
|
||||||
mains = g_list_append(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 {
|
} else {
|
||||||
g_free(ac);
|
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);
|
RETURN_ON_ERROR(error);
|
||||||
|
|
||||||
while ((entryname = g_dir_read_name(directory))) {
|
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);
|
enum psy_type type = power_supply_get_type(entryname);
|
||||||
|
|
||||||
switch (type) {
|
switch (type) {
|
||||||
|
|||||||
+8
-10
@@ -52,11 +52,9 @@ void destroy_button(void *obj)
|
|||||||
Button *button = (Button *)obj;
|
Button *button = (Button *)obj;
|
||||||
if (button->frontend) {
|
if (button->frontend) {
|
||||||
// This is a frontend element
|
// This is a frontend element
|
||||||
if (button->frontend->icon) {
|
free_icon(button->frontend->icon);
|
||||||
imlib_context_set_image(button->frontend->icon);
|
free_icon(button->frontend->icon_hover);
|
||||||
imlib_free_image();
|
free_icon(button->frontend->icon_pressed);
|
||||||
button->frontend->icon = NULL;
|
|
||||||
}
|
|
||||||
button->backend->instances = g_list_remove_all(button->backend->instances, button);
|
button->backend->instances = g_list_remove_all(button->backend->instances, button);
|
||||||
free_and_null(button->frontend);
|
free_and_null(button->frontend);
|
||||||
remove_area(&button->area);
|
remove_area(&button->area);
|
||||||
@@ -78,8 +76,8 @@ void destroy_button(void *obj)
|
|||||||
free_and_null(button->backend->uwheel_command);
|
free_and_null(button->backend->uwheel_command);
|
||||||
|
|
||||||
if (button->backend->instances) {
|
if (button->backend->instances) {
|
||||||
fprintf(stderr, "Error: Attempt to destroy backend while there are still frontend instances!\n");
|
fprintf(stderr, "tint2: Error: Attempt to destroy backend while there are still frontend instances!\n");
|
||||||
exit(-1);
|
exit(EXIT_FAILURE);
|
||||||
}
|
}
|
||||||
free(button->backend);
|
free(button->backend);
|
||||||
free(button);
|
free(button);
|
||||||
@@ -488,7 +486,7 @@ void button_dump_geometry(void *obj, int indent)
|
|||||||
Imlib_Image tmp = imlib_context_get_image();
|
Imlib_Image tmp = imlib_context_get_image();
|
||||||
imlib_context_set_image(button->frontend->icon);
|
imlib_context_set_image(button->frontend->icon);
|
||||||
fprintf(stderr,
|
fprintf(stderr,
|
||||||
"%*sIcon: x = %d, y = %d, w = %d, h = %d\n",
|
"tint2: %*sIcon: x = %d, y = %d, w = %d, h = %d\n",
|
||||||
indent,
|
indent,
|
||||||
"",
|
"",
|
||||||
button->frontend->iconx,
|
button->frontend->iconx,
|
||||||
@@ -499,7 +497,7 @@ void button_dump_geometry(void *obj, int indent)
|
|||||||
imlib_context_set_image(tmp);
|
imlib_context_set_image(tmp);
|
||||||
}
|
}
|
||||||
fprintf(stderr,
|
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,
|
indent,
|
||||||
"",
|
"",
|
||||||
button->frontend->textx,
|
button->frontend->textx,
|
||||||
@@ -530,7 +528,7 @@ void button_action(void *obj, int mouse_button, int x, int y, Time time)
|
|||||||
command = button->backend->dwheel_command;
|
command = button->backend->dwheel_command;
|
||||||
break;
|
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)
|
char *button_get_tooltip(void *obj)
|
||||||
|
|||||||
+6
-6
@@ -154,8 +154,8 @@ int ms_until_second_change(struct timeval* tm)
|
|||||||
void update_clocks_sec(void *arg)
|
void update_clocks_sec(void *arg)
|
||||||
{
|
{
|
||||||
gettimeofday(&time_clock, 0);
|
gettimeofday(&time_clock, 0);
|
||||||
clock_timeout = add_timeout(ms_until_second_change(&time_clock), 0, update_clocks_sec, 0, NULL);
|
|
||||||
update_clocks();
|
update_clocks();
|
||||||
|
clock_timeout = add_timeout(ms_until_second_change(&time_clock), 0, update_clocks_sec, 0, &clock_timeout);
|
||||||
}
|
}
|
||||||
|
|
||||||
void update_clocks_min(void *arg)
|
void update_clocks_min(void *arg)
|
||||||
@@ -164,10 +164,10 @@ void update_clocks_min(void *arg)
|
|||||||
// on next minute change
|
// on next minute change
|
||||||
static time_t old_sec = 0;
|
static time_t old_sec = 0;
|
||||||
gettimeofday(&time_clock, 0);
|
gettimeofday(&time_clock, 0);
|
||||||
clock_timeout = add_timeout(ms_until_second_change(&time_clock), 0, update_clocks_min, 0, NULL);
|
if (time_clock.tv_sec % 60 == 0 || time_clock.tv_sec - old_sec > 60 || (time1_format && !buf_time[0]) || (time2_format && !buf_date[0]))
|
||||||
if (time_clock.tv_sec % 60 == 0 || time_clock.tv_sec - old_sec > 60)
|
|
||||||
update_clocks();
|
update_clocks();
|
||||||
old_sec = time_clock.tv_sec;
|
old_sec = time_clock.tv_sec;
|
||||||
|
clock_timeout = add_timeout(ms_until_second_change(&time_clock), 0, update_clocks_min, 0, &clock_timeout);
|
||||||
}
|
}
|
||||||
|
|
||||||
gboolean time_format_needs_sec_ticks(char *time_format)
|
gboolean time_format_needs_sec_ticks(char *time_format)
|
||||||
@@ -321,9 +321,9 @@ void draw_clock(void *obj, cairo_t *c)
|
|||||||
void clock_dump_geometry(void *obj, int indent)
|
void clock_dump_geometry(void *obj, int indent)
|
||||||
{
|
{
|
||||||
Clock *clock = (Clock *)obj;
|
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) {
|
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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -353,5 +353,5 @@ void clock_action(void *obj, int button, int x, int y, Time time)
|
|||||||
command = clock_dwheel_command;
|
command = clock_dwheel_command;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
tint_exec(command, NULL, NULL, time, obj, x, y);
|
tint_exec(command, NULL, NULL, time, obj, x, y, FALSE, TRUE);
|
||||||
}
|
}
|
||||||
|
|||||||
+59
-29
@@ -119,7 +119,7 @@ void get_action(char *event, MouseAction *action)
|
|||||||
else if (strcmp(event, "prev_task") == 0)
|
else if (strcmp(event, "prev_task") == 0)
|
||||||
*action = PREV_TASK;
|
*action = PREV_TASK;
|
||||||
else
|
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)
|
int get_task_status(char *status)
|
||||||
@@ -215,7 +215,7 @@ void load_launcher_app_dir(const char *path)
|
|||||||
Separator *get_or_create_last_separator()
|
Separator *get_or_create_last_separator()
|
||||||
{
|
{
|
||||||
if (!panel_config.separator_list) {
|
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());
|
panel_config.separator_list = g_list_append(panel_config.separator_list, create_separator());
|
||||||
}
|
}
|
||||||
return (Separator *)g_list_last(panel_config.separator_list)->data;
|
return (Separator *)g_list_last(panel_config.separator_list)->data;
|
||||||
@@ -224,7 +224,7 @@ Separator *get_or_create_last_separator()
|
|||||||
Execp *get_or_create_last_execp()
|
Execp *get_or_create_last_execp()
|
||||||
{
|
{
|
||||||
if (!panel_config.execp_list) {
|
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());
|
panel_config.execp_list = g_list_append(panel_config.execp_list, create_execp());
|
||||||
}
|
}
|
||||||
return (Execp *)g_list_last(panel_config.execp_list)->data;
|
return (Execp *)g_list_last(panel_config.execp_list)->data;
|
||||||
@@ -233,7 +233,7 @@ Execp *get_or_create_last_execp()
|
|||||||
Button *get_or_create_last_button()
|
Button *get_or_create_last_button()
|
||||||
{
|
{
|
||||||
if (!panel_config.button_list) {
|
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());
|
panel_config.button_list = g_list_append(panel_config.button_list, create_button());
|
||||||
}
|
}
|
||||||
return (Button *)g_list_last(panel_config.button_list)->data;
|
return (Button *)g_list_last(panel_config.button_list)->data;
|
||||||
@@ -350,6 +350,12 @@ void add_entry(char *key, char *value)
|
|||||||
id = (id < gradients->len && id >= 0) ? id : -1;
|
id = (id < gradients->len && id >= 0) ? id : -1;
|
||||||
if (id >= 0)
|
if (id >= 0)
|
||||||
bg->gradients[MOUSE_DOWN] = &g_array_index(gradients, GradientClass, id);
|
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 */
|
/* Gradients */
|
||||||
@@ -415,6 +421,7 @@ void add_entry(char *key, char *value)
|
|||||||
}
|
}
|
||||||
} else if (strcmp(key, "panel_items") == 0) {
|
} else if (strcmp(key, "panel_items") == 0) {
|
||||||
new_config_file = TRUE;
|
new_config_file = TRUE;
|
||||||
|
free_and_null(panel_items_order);
|
||||||
panel_items_order = strdup(value);
|
panel_items_order = strdup(value);
|
||||||
systray_enabled = 0;
|
systray_enabled = 0;
|
||||||
launcher_enabled = 0;
|
launcher_enabled = 0;
|
||||||
@@ -432,7 +439,7 @@ void add_entry(char *key, char *value)
|
|||||||
#ifdef ENABLE_BATTERY
|
#ifdef ENABLE_BATTERY
|
||||||
battery_enabled = 1;
|
battery_enabled = 1;
|
||||||
#else
|
#else
|
||||||
fprintf(stderr, "tint2 is build without battery support\n");
|
fprintf(stderr, "tint2: tint2 has been compiled without battery support\n");
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
if (panel_items_order[j] == 'S') {
|
if (panel_items_order[j] == 'S') {
|
||||||
@@ -577,7 +584,7 @@ void add_entry(char *key, char *value)
|
|||||||
bat2_font_desc = pango_font_description_from_string(value);
|
bat2_font_desc = pango_font_description_from_string(value);
|
||||||
bat2_has_font = TRUE;
|
bat2_has_font = TRUE;
|
||||||
#endif
|
#endif
|
||||||
} else if (strcmp (key, "bat1_format") == 0) {
|
} else if (strcmp(key, "bat1_format") == 0) {
|
||||||
#ifdef ENABLE_BATTERY
|
#ifdef ENABLE_BATTERY
|
||||||
if (strlen(value) > 0) {
|
if (strlen(value) > 0) {
|
||||||
free(bat1_format);
|
free(bat1_format);
|
||||||
@@ -585,7 +592,7 @@ void add_entry(char *key, char *value)
|
|||||||
battery_enabled = 1;
|
battery_enabled = 1;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
} else if (strcmp (key, "bat2_format") == 0) {
|
} else if (strcmp(key, "bat2_format") == 0) {
|
||||||
#ifdef ENABLE_BATTERY
|
#ifdef ENABLE_BATTERY
|
||||||
if (strlen(value) > 0) {
|
if (strlen(value) > 0) {
|
||||||
free(bat2_format);
|
free(bat2_format);
|
||||||
@@ -653,7 +660,7 @@ void add_entry(char *key, char *value)
|
|||||||
else if (g_str_equal(value, "dots"))
|
else if (g_str_equal(value, "dots"))
|
||||||
separator->style = SEPARATOR_DOTS;
|
separator->style = SEPARATOR_DOTS;
|
||||||
else
|
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) {
|
} else if (strcmp(key, "separator_size") == 0) {
|
||||||
Separator *separator = get_or_create_last_separator();
|
Separator *separator = get_or_create_last_separator();
|
||||||
separator->thickness = atoi(value);
|
separator->thickness = atoi(value);
|
||||||
@@ -680,7 +687,7 @@ void add_entry(char *key, char *value)
|
|||||||
execp->backend->interval = 0;
|
execp->backend->interval = 0;
|
||||||
int v = atoi(value);
|
int v = atoi(value);
|
||||||
if (v < 0) {
|
if (v < 0) {
|
||||||
fprintf(stderr, "execp_interval must be an integer >= 0\n");
|
fprintf(stderr, "tint2: execp_interval must be an integer >= 0\n");
|
||||||
} else {
|
} else {
|
||||||
execp->backend->interval = v;
|
execp->backend->interval = v;
|
||||||
}
|
}
|
||||||
@@ -736,7 +743,7 @@ void add_entry(char *key, char *value)
|
|||||||
Execp *execp = get_or_create_last_execp();
|
Execp *execp = get_or_create_last_execp();
|
||||||
int v = atoi(value);
|
int v = atoi(value);
|
||||||
if (v < 0) {
|
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 {
|
} else {
|
||||||
execp->backend->icon_w = v;
|
execp->backend->icon_w = v;
|
||||||
}
|
}
|
||||||
@@ -744,7 +751,7 @@ void add_entry(char *key, char *value)
|
|||||||
Execp *execp = get_or_create_last_execp();
|
Execp *execp = get_or_create_last_execp();
|
||||||
int v = atoi(value);
|
int v = atoi(value);
|
||||||
if (v < 0) {
|
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 {
|
} else {
|
||||||
execp->backend->icon_h = v;
|
execp->backend->icon_h = v;
|
||||||
}
|
}
|
||||||
@@ -778,17 +785,23 @@ void add_entry(char *key, char *value)
|
|||||||
/* Button */
|
/* Button */
|
||||||
else if (strcmp(key, "button") == 0) {
|
else if (strcmp(key, "button") == 0) {
|
||||||
panel_config.button_list = g_list_append(panel_config.button_list, create_button());
|
panel_config.button_list = g_list_append(panel_config.button_list, create_button());
|
||||||
} else if (strcmp(key, "button_icon") == 0 && strlen(value)) {
|
} else if (strcmp(key, "button_icon") == 0) {
|
||||||
Button *button = get_or_create_last_button();
|
if (strlen(value)) {
|
||||||
button->backend->icon_name = strdup(value);
|
Button *button = get_or_create_last_button();
|
||||||
} else if (strcmp(key, "button_text") == 0 && strlen(value)) {
|
button->backend->icon_name = strdup(value);
|
||||||
Button *button = get_or_create_last_button();
|
}
|
||||||
free_and_null(button->backend->text);
|
} else if (strcmp(key, "button_text") == 0) {
|
||||||
button->backend->text = strdup(value);
|
if (strlen(value)) {
|
||||||
} else if (strcmp(key, "button_tooltip") == 0 && strlen(value)) {
|
Button *button = get_or_create_last_button();
|
||||||
Button *button = get_or_create_last_button();
|
free_and_null(button->backend->text);
|
||||||
free_and_null(button->backend->tooltip);
|
button->backend->text = strdup(value);
|
||||||
button->backend->tooltip = 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) {
|
} else if (strcmp(key, "button_font") == 0) {
|
||||||
Button *button = get_or_create_last_button();
|
Button *button = get_or_create_last_button();
|
||||||
pango_font_description_free(button->backend->font_desc);
|
pango_font_description_free(button->backend->font_desc);
|
||||||
@@ -1079,11 +1092,18 @@ void add_entry(char *key, char *value)
|
|||||||
panel_config.g_task.config_background_mask |= (1 << status);
|
panel_config.g_task.config_background_mask |= (1 << status);
|
||||||
if (status == TASK_NORMAL)
|
if (status == TASK_NORMAL)
|
||||||
panel_config.g_task.area.bg = panel_config.g_task.background[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
|
// "tooltip" is deprecated but here for backwards compatibility
|
||||||
else if (strcmp(key, "task_tooltip") == 0 || strcmp(key, "tooltip") == 0)
|
else if (strcmp(key, "task_tooltip") == 0 || strcmp(key, "tooltip") == 0)
|
||||||
panel_config.g_task.tooltip_enabled = atoi(value);
|
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 */
|
/* Systray */
|
||||||
else if (strcmp(key, "systray_padding") == 0) {
|
else if (strcmp(key, "systray_padding") == 0) {
|
||||||
@@ -1126,8 +1146,11 @@ void add_entry(char *key, char *value)
|
|||||||
} else if (strcmp(key, "systray_monitor") == 0) {
|
} else if (strcmp(key, "systray_monitor") == 0) {
|
||||||
systray_monitor = MAX(0, config_get_monitor(value));
|
systray_monitor = MAX(0, config_get_monitor(value));
|
||||||
} else if (strcmp(key, "systray_name_filter") == 0) {
|
} 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);
|
free(systray_hide_name_filter);
|
||||||
|
}
|
||||||
systray_hide_name_filter = strdup(value);
|
systray_hide_name_filter = strdup(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1280,8 +1303,13 @@ void add_entry(char *key, char *value)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
else
|
else if (strcmp(key, "primary_monitor_first") == 0) {
|
||||||
fprintf(stderr, "tint2 : invalid option \"%s\",\n upgrade tint2 or correct your config file\n", key);
|
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)
|
if (value1)
|
||||||
free(value1);
|
free(value1);
|
||||||
@@ -1293,6 +1321,8 @@ void add_entry(char *key, char *value)
|
|||||||
|
|
||||||
gboolean config_read_file(const char *path)
|
gboolean config_read_file(const char *path)
|
||||||
{
|
{
|
||||||
|
fprintf(stderr, "tint2: Loading config file: %s\n", path);
|
||||||
|
|
||||||
FILE *fp = fopen(path, "r");
|
FILE *fp = fopen(path, "r");
|
||||||
if (!fp)
|
if (!fp)
|
||||||
return FALSE;
|
return FALSE;
|
||||||
@@ -1361,12 +1391,12 @@ gboolean config_read_default_path()
|
|||||||
|
|
||||||
// copy tint2rc from system directory to user directory
|
// 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
|
// According to the XDG Base Directory Specification
|
||||||
// (https://specifications.freedesktop.org/basedir-spec/basedir-spec-0.6.html)
|
// (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 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))
|
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;
|
gchar *path2 = 0;
|
||||||
system_dirs = g_get_system_config_dirs();
|
system_dirs = g_get_system_config_dirs();
|
||||||
@@ -1383,7 +1413,7 @@ gboolean config_read_default_path()
|
|||||||
// copy file in user directory (path1)
|
// copy file in user directory (path1)
|
||||||
gchar *dir = g_build_filename(g_get_user_config_dir(), "tint2", NULL);
|
gchar *dir = g_build_filename(g_get_user_config_dir(), "tint2", NULL);
|
||||||
if (!g_file_test(dir, G_FILE_TEST_IS_DIR))
|
if (!g_file_test(dir, G_FILE_TEST_IS_DIR))
|
||||||
g_mkdir(dir, 0700);
|
g_mkdir_with_parents(dir, 0700);
|
||||||
g_free(dir);
|
g_free(dir);
|
||||||
|
|
||||||
path1 = g_build_filename(g_get_user_config_dir(), "tint2", "tint2rc", NULL);
|
path1 = g_build_filename(g_get_user_config_dir(), "tint2", "tint2rc", NULL);
|
||||||
@@ -1399,7 +1429,7 @@ gboolean config_read_default_path()
|
|||||||
// generate config file
|
// generate config file
|
||||||
gchar *dir = g_build_filename(g_get_user_config_dir(), "tint2", NULL);
|
gchar *dir = g_build_filename(g_get_user_config_dir(), "tint2", NULL);
|
||||||
if (!g_file_test(dir, G_FILE_TEST_IS_DIR))
|
if (!g_file_test(dir, G_FILE_TEST_IS_DIR))
|
||||||
g_mkdir(dir, 0700);
|
g_mkdir_with_parents(dir, 0700);
|
||||||
g_free(dir);
|
g_free(dir);
|
||||||
|
|
||||||
path1 = g_build_filename(g_get_user_config_dir(), "tint2", "tint2rc", NULL);
|
path1 = g_build_filename(g_get_user_config_dir(), "tint2", "tint2rc", NULL);
|
||||||
|
|||||||
@@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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
|
||||||
+33
-21
@@ -70,12 +70,9 @@ void destroy_execp(void *obj)
|
|||||||
stop_timeout(execp->backend->timer);
|
stop_timeout(execp->backend->timer);
|
||||||
execp->backend->timer = NULL;
|
execp->backend->timer = NULL;
|
||||||
|
|
||||||
if (execp->backend->icon) {
|
free_icon(execp->backend->icon);
|
||||||
imlib_context_set_image(execp->backend->icon);
|
|
||||||
imlib_free_image();
|
|
||||||
execp->backend->icon = NULL;
|
|
||||||
}
|
|
||||||
free_and_null(execp->backend->buf_stdout);
|
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->text);
|
||||||
free_and_null(execp->backend->icon_path);
|
free_and_null(execp->backend->icon_path);
|
||||||
if (execp->backend->child) {
|
if (execp->backend->child) {
|
||||||
@@ -107,8 +104,8 @@ void destroy_execp(void *obj)
|
|||||||
free_and_null(execp->backend->uwheel_command);
|
free_and_null(execp->backend->uwheel_command);
|
||||||
|
|
||||||
if (execp->backend->instances) {
|
if (execp->backend->instances) {
|
||||||
fprintf(stderr, "Error: Attempt to destroy backend while there are still frontend instances!\n");
|
fprintf(stderr, "tint2: Error: Attempt to destroy backend while there are still frontend instances!\n");
|
||||||
exit(-1);
|
exit(EXIT_FAILURE);
|
||||||
}
|
}
|
||||||
free(execp->backend);
|
free(execp->backend);
|
||||||
free(execp);
|
free(execp);
|
||||||
@@ -195,8 +192,7 @@ void init_execp_panel(void *p)
|
|||||||
execp->area.on_screen = TRUE;
|
execp->area.on_screen = TRUE;
|
||||||
instantiate_area_gradients(&execp->area);
|
instantiate_area_gradients(&execp->area);
|
||||||
|
|
||||||
if (!execp->backend->timer)
|
execp->backend->timer = add_timeout(10, 0, execp_timer_callback, execp, &execp->backend->timer);
|
||||||
execp->backend->timer = add_timeout(10, 0, execp_timer_callback, execp, &execp->backend->timer);
|
|
||||||
|
|
||||||
execp_update_post_read(execp);
|
execp_update_post_read(execp);
|
||||||
}
|
}
|
||||||
@@ -363,10 +359,12 @@ void execp_compute_icon_text_geometry(Execp *execp,
|
|||||||
if (*icon_w)
|
if (*icon_w)
|
||||||
*new_size += *interior_padding + *icon_w;
|
*new_size += *interior_padding + *icon_w;
|
||||||
*new_size += 2 * *horiz_padding + left_right_border_width(area);
|
*new_size += 2 * *horiz_padding + left_right_border_width(area);
|
||||||
if (*new_size > area->width || *new_size < (area->width - 6)) {
|
if (*new_size < area->width && abs(*new_size - area->width) < 6) {
|
||||||
// we try to limit the number of resize
|
// we try to limit the number of resizes
|
||||||
*new_size += 1;
|
*new_size = area->width;
|
||||||
*resized = TRUE;
|
*resized = TRUE;
|
||||||
|
} else {
|
||||||
|
*resized = *new_size != area->width;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (!*text_next_line) {
|
if (!*text_next_line) {
|
||||||
@@ -516,7 +514,7 @@ void execp_dump_geometry(void *obj, int indent)
|
|||||||
Imlib_Image tmp = imlib_context_get_image();
|
Imlib_Image tmp = imlib_context_get_image();
|
||||||
imlib_context_set_image(execp->backend->icon);
|
imlib_context_set_image(execp->backend->icon);
|
||||||
fprintf(stderr,
|
fprintf(stderr,
|
||||||
"%*sIcon: x = %d, y = %d, w = %d, h = %d\n",
|
"tint2: %*sIcon: x = %d, y = %d, w = %d, h = %d\n",
|
||||||
indent,
|
indent,
|
||||||
"",
|
"",
|
||||||
execp->frontend->iconx,
|
execp->frontend->iconx,
|
||||||
@@ -527,7 +525,7 @@ void execp_dump_geometry(void *obj, int indent)
|
|||||||
imlib_context_set_image(tmp);
|
imlib_context_set_image(tmp);
|
||||||
}
|
}
|
||||||
fprintf(stderr,
|
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,
|
indent,
|
||||||
"",
|
"",
|
||||||
execp->frontend->textx,
|
execp->frontend->textx,
|
||||||
@@ -575,7 +573,7 @@ void execp_action(void *obj, int button, int x, int y, Time time)
|
|||||||
setenvd("EXECP_Y", y);
|
setenvd("EXECP_Y", y);
|
||||||
setenvd("EXECP_W", execp->area.width);
|
setenvd("EXECP_W", execp->area.width);
|
||||||
setenvd("EXECP_H", execp->area.height);
|
setenvd("EXECP_H", execp->area.height);
|
||||||
pid_t pid = tint_exec(command, NULL, NULL, time, obj, x, y);
|
pid_t pid = tint_exec(command, NULL, NULL, time, obj, x, y, FALSE, TRUE);
|
||||||
unsetenv("EXECP_X");
|
unsetenv("EXECP_X");
|
||||||
unsetenv("EXECP_Y");
|
unsetenv("EXECP_Y");
|
||||||
unsetenv("EXECP_W");
|
unsetenv("EXECP_W");
|
||||||
@@ -607,7 +605,7 @@ void execp_timer_callback(void *arg)
|
|||||||
int pipe_fd_stdout[2];
|
int pipe_fd_stdout[2];
|
||||||
if (pipe(pipe_fd_stdout)) {
|
if (pipe(pipe_fd_stdout)) {
|
||||||
// TODO maybe write this in tooltip, but if this happens we're screwed anyways
|
// 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;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -618,7 +616,7 @@ void execp_timer_callback(void *arg)
|
|||||||
close(pipe_fd_stdout[1]);
|
close(pipe_fd_stdout[1]);
|
||||||
close(pipe_fd_stdout[0]);
|
close(pipe_fd_stdout[0]);
|
||||||
// TODO maybe write this in tooltip, but if this happens we're screwed anyways
|
// 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;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -628,14 +626,14 @@ void execp_timer_callback(void *arg)
|
|||||||
pid_t child = fork();
|
pid_t child = fork();
|
||||||
if (child == -1) {
|
if (child == -1) {
|
||||||
// TODO maybe write this in tooltip, but if this happens we're screwed anyways
|
// TODO maybe write this in tooltip, but if this happens we're screwed anyways
|
||||||
fprintf(stderr, "Fork failed.\n");
|
fprintf(stderr, "tint2: Fork failed.\n");
|
||||||
close(pipe_fd_stdout[1]);
|
close(pipe_fd_stdout[1]);
|
||||||
close(pipe_fd_stdout[0]);
|
close(pipe_fd_stdout[0]);
|
||||||
close(pipe_fd_stderr[1]);
|
close(pipe_fd_stderr[1]);
|
||||||
close(pipe_fd_stderr[0]);
|
close(pipe_fd_stderr[0]);
|
||||||
return;
|
return;
|
||||||
} else if (child == 0) {
|
} 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
|
// We are in the child
|
||||||
close(pipe_fd_stdout[0]);
|
close(pipe_fd_stdout[0]);
|
||||||
dup2(pipe_fd_stdout[1], 1); // 1 is stdout
|
dup2(pipe_fd_stdout[1], 1); // 1 is stdout
|
||||||
@@ -643,11 +641,11 @@ void execp_timer_callback(void *arg)
|
|||||||
close(pipe_fd_stderr[0]);
|
close(pipe_fd_stderr[0]);
|
||||||
dup2(pipe_fd_stderr[1], 2); // 2 is stderr
|
dup2(pipe_fd_stderr[1], 2); // 2 is stderr
|
||||||
close(pipe_fd_stderr[1]);
|
close(pipe_fd_stderr[1]);
|
||||||
|
close_all_fds();
|
||||||
setpgid(0, 0);
|
setpgid(0, 0);
|
||||||
execl("/bin/sh", "/bin/sh", "-c", execp->backend->command, NULL);
|
execl("/bin/sh", "/bin/sh", "-c", execp->backend->command, NULL);
|
||||||
// This should never happen!
|
// This should never happen!
|
||||||
fprintf(stdout, "execl() failed\nexecl() failed\n");
|
fprintf(stderr, "execl() failed\nexecl() failed\n");
|
||||||
fflush(stdout);
|
|
||||||
exit(0);
|
exit(0);
|
||||||
}
|
}
|
||||||
close(pipe_fd_stdout[1]);
|
close(pipe_fd_stdout[1]);
|
||||||
@@ -985,3 +983,17 @@ void execp_update_post_read(Execp *execp)
|
|||||||
schedule_panel_redraw();
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -148,4 +148,6 @@ void execp_update_post_read(Execp *execp);
|
|||||||
|
|
||||||
void execp_default_font_changed();
|
void execp_default_font_changed();
|
||||||
|
|
||||||
|
void handle_execp_events();
|
||||||
|
|
||||||
#endif // EXECPLUGIN_H
|
#endif // EXECPLUGIN_H
|
||||||
|
|||||||
+294
@@ -0,0 +1,294 @@
|
|||||||
|
#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 "drag_and_drop.h"
|
||||||
|
#include "fps_distribution.h"
|
||||||
|
#include "panel.h"
|
||||||
|
#include "server.h"
|
||||||
|
#include "signals.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], "-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;
|
||||||
|
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 timeout *detect_compositor_timer = NULL;
|
||||||
|
static int detect_compositor_timer_counter = 0;
|
||||||
|
|
||||||
|
void detect_compositor(void *arg)
|
||||||
|
{
|
||||||
|
if (server.composite_manager) {
|
||||||
|
stop_timeout(detect_compositor_timer);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
detect_compositor_timer_counter--;
|
||||||
|
if (detect_compositor_timer_counter < 0) {
|
||||||
|
stop_timeout(detect_compositor_timer);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// No compositor, check for one
|
||||||
|
if (XGetSelectionOwner(server.display, server.atom._NET_WM_CM_S0) != None) {
|
||||||
|
stop_timeout(detect_compositor_timer);
|
||||||
|
// 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;
|
||||||
|
|
||||||
|
stop_timeout(detect_compositor_timer);
|
||||||
|
// Check every 0.5 seconds for up to 30 seconds
|
||||||
|
detect_compositor_timer_counter = 60;
|
||||||
|
detect_compositor_timer = add_timeout(500, 500, detect_compositor, 0, &detect_compositor_timer);
|
||||||
|
}
|
||||||
|
|
||||||
|
void create_default_elements()
|
||||||
|
{
|
||||||
|
default_timeout();
|
||||||
|
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 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 icon
|
||||||
|
const gchar *const *data_dirs = g_get_system_data_dirs();
|
||||||
|
for (int i = 0; data_dirs[i] != NULL; i++) {
|
||||||
|
gchar *path = g_build_filename(data_dirs[i], "tint2", "default_icon.png", NULL);
|
||||||
|
if (g_file_test(path, G_FILE_TEST_EXISTS))
|
||||||
|
default_icon = load_image(path, TRUE);
|
||||||
|
g_free(path);
|
||||||
|
}
|
||||||
|
if (!default_icon) {
|
||||||
|
fprintf(stderr,
|
||||||
|
RED "Could not load default_icon.png. Please check that tint2 has been installed correctly!" RESET
|
||||||
|
"\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
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_cli_arguments(argc, argv);
|
||||||
|
create_default_elements();
|
||||||
|
init_signals();
|
||||||
|
handle_env_vars();
|
||||||
|
|
||||||
|
init_X11_pre_config();
|
||||||
|
if (!config_read()) {
|
||||||
|
fprintf(stderr, "tint2: Could not read config file.\n");
|
||||||
|
print_usage();
|
||||||
|
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_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_timeout();
|
||||||
|
|
||||||
|
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
|
||||||
|
}
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
#ifndef INIT_H
|
||||||
|
#define INIT_H
|
||||||
|
|
||||||
|
void init(int argc, char **argv);
|
||||||
|
void cleanup();
|
||||||
|
|
||||||
|
#endif
|
||||||
@@ -96,6 +96,10 @@ void expand_exec(DesktopEntry *entry, const char *path)
|
|||||||
q += strlen("''");
|
q += strlen("''");
|
||||||
q += strlen(path);
|
q += strlen(path);
|
||||||
q--; // To balance the q++ in the for
|
q--; // To balance the q++ in the for
|
||||||
|
} else if (*p == 'f' || *p == 'F') {
|
||||||
|
sprintf(q, "%c%c", '%', *p);
|
||||||
|
q += 2;
|
||||||
|
q--; // To balance the q++ in the for
|
||||||
} else {
|
} else {
|
||||||
// We don't care about other expansions
|
// We don't care about other expansions
|
||||||
q--; // Delete the last % from q
|
q--; // Delete the last % from q
|
||||||
@@ -113,10 +117,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->name = entry->generic_name = entry->icon = entry->exec = entry->cwd = NULL;
|
||||||
entry->hidden_from_menus = FALSE;
|
entry->hidden_from_menus = FALSE;
|
||||||
|
entry->start_in_terminal = FALSE;
|
||||||
|
entry->startup_notification = TRUE;
|
||||||
|
|
||||||
FILE *fp = fopen(path, "rt");
|
FILE *fp = fopen(path, "rt");
|
||||||
if (fp == NULL) {
|
if (fp == NULL) {
|
||||||
fprintf(stderr, "Could not open file %s\n", path);
|
fprintf(stderr, "tint2: Could not open file %s\n", path);
|
||||||
return FALSE;
|
return FALSE;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -126,14 +132,14 @@ gboolean read_desktop_file_full_path(const char *path, DesktopEntry *entry)
|
|||||||
int lang_index_default = 1;
|
int lang_index_default = 1;
|
||||||
#define LANG_DBG 0
|
#define LANG_DBG 0
|
||||||
if (LANG_DBG)
|
if (LANG_DBG)
|
||||||
printf("Languages:");
|
fprintf(stderr, "tint2: Languages:");
|
||||||
for (int i = 0; languages[i]; i++) {
|
for (int i = 0; languages[i]; i++) {
|
||||||
lang_index_default = i + 1;
|
lang_index_default = i + 1;
|
||||||
if (LANG_DBG)
|
if (LANG_DBG)
|
||||||
printf(" %s", languages[i]);
|
fprintf(stderr, "tint2: %s", languages[i]);
|
||||||
}
|
}
|
||||||
if (LANG_DBG)
|
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
|
// 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_name = lang_index_default + 1;
|
||||||
int lang_index_generic_name = lang_index_default + 1;
|
int lang_index_generic_name = lang_index_default + 1;
|
||||||
@@ -192,6 +198,10 @@ gboolean read_desktop_file_full_path(const char *path, DesktopEntry *entry)
|
|||||||
entry->icon = strdup(value);
|
entry->icon = strdup(value);
|
||||||
} else if (strcmp(key, "NoDisplay") == 0) {
|
} else if (strcmp(key, "NoDisplay") == 0) {
|
||||||
entry->hidden_from_menus = strcasecmp(value, "true") == 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 +288,11 @@ void free_desktop_entry(DesktopEntry *entry)
|
|||||||
|
|
||||||
void test_read_desktop_file()
|
void test_read_desktop_file()
|
||||||
{
|
{
|
||||||
fprintf(stdout, "\033[1;33m");
|
fprintf(stderr, YELLOW);
|
||||||
DesktopEntry entry;
|
DesktopEntry entry;
|
||||||
read_desktop_file("/usr/share/applications/firefox.desktop", &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(stderr, "tint2: Name:%s GenericName:%s Icon:%s Exec:%s\n", entry.name, entry.generic_name, entry.icon, entry.exec);
|
||||||
fprintf(stdout, "\033[0m");
|
fprintf(stderr, RESET);
|
||||||
}
|
}
|
||||||
|
|
||||||
GSList *apps_locations = NULL;
|
GSList *apps_locations = NULL;
|
||||||
|
|||||||
@@ -17,6 +17,8 @@ typedef struct DesktopEntry {
|
|||||||
char *path;
|
char *path;
|
||||||
char *cwd;
|
char *cwd;
|
||||||
gboolean hidden_from_menus;
|
gboolean hidden_from_menus;
|
||||||
|
gboolean start_in_terminal;
|
||||||
|
gboolean startup_notification;
|
||||||
} DesktopEntry;
|
} DesktopEntry;
|
||||||
|
|
||||||
// Parses a line of the form "key = value". Modifies the line.
|
// Parses a line of the form "key = value". Modifies the line.
|
||||||
|
|||||||
@@ -29,6 +29,8 @@
|
|||||||
#include "common.h"
|
#include "common.h"
|
||||||
#include "cache.h"
|
#include "cache.h"
|
||||||
|
|
||||||
|
gboolean debug_icons = FALSE;
|
||||||
|
|
||||||
#define ICON_DIR_TYPE_SCALABLE 0
|
#define ICON_DIR_TYPE_SCALABLE 0
|
||||||
#define ICON_DIR_TYPE_FIXED 1
|
#define ICON_DIR_TYPE_FIXED 1
|
||||||
#define ICON_DIR_TYPE_THRESHOLD 2
|
#define ICON_DIR_TYPE_THRESHOLD 2
|
||||||
@@ -90,7 +92,7 @@ IconTheme *load_theme_from_index(const char *file_name, const char *name)
|
|||||||
size_t line_size;
|
size_t line_size;
|
||||||
|
|
||||||
if ((f = fopen(file_name, "rt")) == NULL) {
|
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;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -325,22 +327,22 @@ void free_themes(IconThemeWrapper *wrapper)
|
|||||||
|
|
||||||
void test_launcher_read_theme_file()
|
void test_launcher_read_theme_file()
|
||||||
{
|
{
|
||||||
fprintf(stdout, "\033[1;33m");
|
fprintf(stdout, YELLOW);
|
||||||
IconTheme *theme = load_theme("oxygen");
|
IconTheme *theme = load_theme("oxygen");
|
||||||
if (!theme) {
|
if (!theme) {
|
||||||
printf("Could not load theme\n");
|
fprintf(stderr, "tint2: Could not load theme\n");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
printf("Loaded theme: %s\n", theme->name);
|
fprintf(stderr, "tint2: Loaded theme: %s\n", theme->name);
|
||||||
GSList *item = theme->list_inherits;
|
GSList *item = theme->list_inherits;
|
||||||
while (item != NULL) {
|
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 = g_slist_next(item);
|
||||||
}
|
}
|
||||||
item = theme->list_directories;
|
item = theme->list_directories;
|
||||||
while (item != NULL) {
|
while (item != NULL) {
|
||||||
IconThemeDir *dir = item->data;
|
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->name,
|
||||||
dir->size,
|
dir->size,
|
||||||
dir->min_size,
|
dir->min_size,
|
||||||
@@ -352,7 +354,7 @@ void test_launcher_read_theme_file()
|
|||||||
: "?????");
|
: "?????");
|
||||||
item = g_slist_next(item);
|
item = g_slist_next(item);
|
||||||
}
|
}
|
||||||
fprintf(stdout, "\033[0m");
|
fprintf(stdout, RESET);
|
||||||
}
|
}
|
||||||
|
|
||||||
gboolean str_list_contains(const GSList *list, const char *value)
|
gboolean str_list_contains(const GSList *list, const char *value)
|
||||||
@@ -379,7 +381,7 @@ void load_themes_helper(const char *name, GSList **themes, GSList **queued)
|
|||||||
char *queued_name = queue->data;
|
char *queued_name = queue->data;
|
||||||
queue = g_slist_remove(queue, queued_name);
|
queue = g_slist_remove(queue, queued_name);
|
||||||
|
|
||||||
fprintf(stderr, " '%s',", queued_name);
|
fprintf(stderr, "tint2: '%s',", queued_name);
|
||||||
IconTheme *theme = load_theme(queued_name);
|
IconTheme *theme = load_theme(queued_name);
|
||||||
if (theme != NULL) {
|
if (theme != NULL) {
|
||||||
*themes = g_slist_append(*themes, theme);
|
*themes = g_slist_append(*themes, theme);
|
||||||
@@ -399,7 +401,7 @@ void load_themes_helper(const char *name, GSList **themes, GSList **queued)
|
|||||||
|
|
||||||
free(queued_name);
|
free(queued_name);
|
||||||
}
|
}
|
||||||
fprintf(stderr, "\n");
|
fprintf(stderr, "tint2: \n");
|
||||||
|
|
||||||
// Free the queue
|
// Free the queue
|
||||||
GSList *l;
|
GSList *l;
|
||||||
@@ -413,7 +415,7 @@ void load_default_theme(IconThemeWrapper *wrapper)
|
|||||||
if (wrapper->_themes_loaded)
|
if (wrapper->_themes_loaded)
|
||||||
return;
|
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(wrapper->icon_theme_name, &wrapper->themes, &wrapper->_queued);
|
||||||
load_themes_helper("hicolor", &wrapper->themes, &wrapper->_queued);
|
load_themes_helper("hicolor", &wrapper->themes, &wrapper->_queued);
|
||||||
@@ -426,7 +428,7 @@ void load_fallbacks(IconThemeWrapper *wrapper)
|
|||||||
if (wrapper->_fallback_loaded)
|
if (wrapper->_fallback_loaded)
|
||||||
return;
|
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
|
// Load wrapper->themes_fallback
|
||||||
const GSList *location;
|
const GSList *location;
|
||||||
@@ -459,7 +461,7 @@ void load_icon_cache(IconThemeWrapper *wrapper)
|
|||||||
if (wrapper->_cache.loaded)
|
if (wrapper->_cache.loaded)
|
||||||
return;
|
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();
|
gchar *cache_path = get_icon_cache_path();
|
||||||
load_cache(&wrapper->_cache, cache_path);
|
load_cache(&wrapper->_cache, cache_path);
|
||||||
@@ -471,7 +473,7 @@ void save_icon_cache(IconThemeWrapper *wrapper)
|
|||||||
if (!wrapper || !wrapper->_cache.dirty)
|
if (!wrapper || !wrapper->_cache.dirty)
|
||||||
return;
|
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();
|
gchar *cache_path = get_icon_cache_path();
|
||||||
save_cache(&wrapper->_cache, cache_path);
|
save_cache(&wrapper->_cache, cache_path);
|
||||||
g_free(cache_path);
|
g_free(cache_path);
|
||||||
@@ -482,7 +484,7 @@ IconThemeWrapper *load_themes(const char *icon_theme_name)
|
|||||||
IconThemeWrapper *wrapper = calloc(1, sizeof(IconThemeWrapper));
|
IconThemeWrapper *wrapper = calloc(1, sizeof(IconThemeWrapper));
|
||||||
|
|
||||||
if (!icon_theme_name) {
|
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";
|
icon_theme_name = "hicolor";
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -533,7 +535,6 @@ gint compare_theme_directories(gconstpointer a, gconstpointer b, gpointer size_q
|
|||||||
return abs(da->size - size) - abs(db->size - size);
|
return abs(da->size - size) - abs(db->size - size);
|
||||||
}
|
}
|
||||||
|
|
||||||
#define DEBUG_ICON_SEARCH 0
|
|
||||||
char *get_icon_path_helper(GSList *themes, const char *icon_name, int size)
|
char *get_icon_path_helper(GSList *themes, const char *icon_name, int size)
|
||||||
{
|
{
|
||||||
if (icon_name == NULL)
|
if (icon_name == NULL)
|
||||||
@@ -585,6 +586,8 @@ char *get_icon_path_helper(GSList *themes, const char *icon_name, int size)
|
|||||||
char *file_name = calloc(file_name_size, 1);
|
char *file_name = calloc(file_name_size, 1);
|
||||||
|
|
||||||
for (theme = themes; theme; theme = g_slist_next(theme)) {
|
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 =
|
((IconTheme *)theme->data)->list_directories =
|
||||||
g_slist_sort_with_data(((IconTheme *)theme->data)->list_directories,
|
g_slist_sort_with_data(((IconTheme *)theme->data)->list_directories,
|
||||||
compare_theme_directories,
|
compare_theme_directories,
|
||||||
@@ -600,6 +603,8 @@ char *get_icon_path_helper(GSList *themes, const char *icon_name, int size)
|
|||||||
(!next_larger_theme ? 1 : theme == next_larger_theme));
|
(!next_larger_theme ? 1 : theme == next_larger_theme));
|
||||||
if (!possible)
|
if (!possible)
|
||||||
continue;
|
continue;
|
||||||
|
if (debug_icons)
|
||||||
|
fprintf(stderr, "tint2: Searching directory: %s\n", ((IconThemeDir *)dir->data)->name);
|
||||||
const GSList *base;
|
const GSList *base;
|
||||||
for (base = basenames; base; base = g_slist_next(base)) {
|
for (base = basenames; base; base = g_slist_next(base)) {
|
||||||
for (GSList *ext = extensions; ext; ext = g_slist_next(ext)) {
|
for (GSList *ext = extensions; ext; ext = g_slist_next(ext)) {
|
||||||
@@ -617,11 +622,11 @@ char *get_icon_path_helper(GSList *themes, const char *icon_name, int size)
|
|||||||
file_name[0] = 0;
|
file_name[0] = 0;
|
||||||
// filename = directory/$(themename)/subdirectory/iconname.extension
|
// filename = directory/$(themename)/subdirectory/iconname.extension
|
||||||
sprintf(file_name, "%s/%s/%s/%s%s", base_name, theme_name, dir_name, icon_name, extension);
|
sprintf(file_name, "%s/%s/%s/%s%s", base_name, theme_name, dir_name, icon_name, extension);
|
||||||
if (DEBUG_ICON_SEARCH)
|
if (debug_icons)
|
||||||
printf("checking %s\n", file_name);
|
fprintf(stderr, "tint2: Checking %s\n", file_name);
|
||||||
if (g_file_test(file_name, G_FILE_TEST_EXISTS)) {
|
if (g_file_test(file_name, G_FILE_TEST_EXISTS)) {
|
||||||
if (DEBUG_ICON_SEARCH)
|
if (debug_icons)
|
||||||
printf("found: %s\n", file_name);
|
fprintf(stderr, "tint2: Found potential match: %s\n", file_name);
|
||||||
// Closest match
|
// Closest match
|
||||||
if (directory_size_distance((IconThemeDir *)dir->data, size) < minimal_size &&
|
if (directory_size_distance((IconThemeDir *)dir->data, size) < minimal_size &&
|
||||||
(!best_file_theme ? 1 : theme == best_file_theme)) {
|
(!best_file_theme ? 1 : theme == best_file_theme)) {
|
||||||
@@ -632,8 +637,8 @@ char *get_icon_path_helper(GSList *themes, const char *icon_name, int size)
|
|||||||
best_file_name = strdup(file_name);
|
best_file_name = strdup(file_name);
|
||||||
minimal_size = directory_size_distance((IconThemeDir *)dir->data, size);
|
minimal_size = directory_size_distance((IconThemeDir *)dir->data, size);
|
||||||
best_file_theme = theme;
|
best_file_theme = theme;
|
||||||
if (DEBUG_ICON_SEARCH)
|
if (debug_icons)
|
||||||
printf("best_file_name = %s; minimal_size = %d\n", best_file_name, minimal_size);
|
fprintf(stderr, "tint2: best_file_name = %s; minimal_size = %d\n", best_file_name, minimal_size);
|
||||||
}
|
}
|
||||||
// Next larger match
|
// Next larger match
|
||||||
if (((IconThemeDir *)dir->data)->size >= size &&
|
if (((IconThemeDir *)dir->data)->size >= size &&
|
||||||
@@ -646,8 +651,8 @@ char *get_icon_path_helper(GSList *themes, const char *icon_name, int size)
|
|||||||
next_larger = strdup(file_name);
|
next_larger = strdup(file_name);
|
||||||
next_larger_size = ((IconThemeDir *)dir->data)->size;
|
next_larger_size = ((IconThemeDir *)dir->data)->size;
|
||||||
next_larger_theme = theme;
|
next_larger_theme = theme;
|
||||||
if (DEBUG_ICON_SEARCH)
|
if (debug_icons)
|
||||||
printf("next_larger = %s; next_larger_size = %d\n", next_larger, next_larger_size);
|
fprintf(stderr, "tint2: next_larger = %s; next_larger_size = %d\n", next_larger, next_larger_size);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -668,6 +673,8 @@ char *get_icon_path_helper(GSList *themes, const char *icon_name, int size)
|
|||||||
|
|
||||||
// Look in unthemed icons
|
// 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 (const GSList *base = basenames; base; base = g_slist_next(base)) {
|
||||||
for (GSList *ext = extensions; ext; ext = g_slist_next(ext)) {
|
for (GSList *ext = extensions; ext; ext = g_slist_next(ext)) {
|
||||||
char *base_name = (char *)base->data;
|
char *base_name = (char *)base->data;
|
||||||
@@ -675,9 +682,11 @@ char *get_icon_path_helper(GSList *themes, const char *icon_name, int size)
|
|||||||
file_name = calloc(strlen(base_name) + strlen(icon_name) + strlen(extension) + 100, 1);
|
file_name = calloc(strlen(base_name) + strlen(icon_name) + strlen(extension) + 100, 1);
|
||||||
// filename = directory/iconname.extension
|
// filename = directory/iconname.extension
|
||||||
sprintf(file_name, "%s/%s%s", base_name, icon_name, extension);
|
sprintf(file_name, "%s/%s%s", base_name, icon_name, extension);
|
||||||
if (DEBUG_ICON_SEARCH)
|
if (debug_icons)
|
||||||
printf("checking %s\n", file_name);
|
fprintf(stderr, "tint2: Checking %s\n", file_name);
|
||||||
if (g_file_test(file_name, G_FILE_TEST_EXISTS)) {
|
if (g_file_test(file_name, G_FILE_TEST_EXISTS)) {
|
||||||
|
if (debug_icons)
|
||||||
|
fprintf(stderr, "tint2: Found %s\n", file_name);
|
||||||
g_slist_free(extensions);
|
g_slist_free(extensions);
|
||||||
return file_name;
|
return file_name;
|
||||||
} else {
|
} else {
|
||||||
@@ -715,7 +724,7 @@ char *get_icon_path_from_cache(IconThemeWrapper *wrapper, const char *icon_name,
|
|||||||
if (!g_file_test(value, G_FILE_TEST_EXISTS))
|
if (!g_file_test(value, G_FILE_TEST_EXISTS))
|
||||||
return NULL;
|
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);
|
// wrapper->icon_theme_name, icon_name, size, value);
|
||||||
|
|
||||||
return strdup(value);
|
return strdup(value);
|
||||||
@@ -742,28 +751,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)
|
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;
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
if (!icon_name || strlen(icon_name) == 0)
|
if (!icon_name || strlen(icon_name) == 0)
|
||||||
goto notfound;
|
goto notfound;
|
||||||
|
|
||||||
char *path = get_icon_path_from_cache(wrapper, icon_name, size);
|
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;
|
return path;
|
||||||
|
}
|
||||||
|
|
||||||
load_default_theme(wrapper);
|
load_default_theme(wrapper);
|
||||||
|
|
||||||
icon_name = icon_name ? icon_name : DEFAULT_ICON;
|
icon_name = icon_name ? icon_name : DEFAULT_ICON;
|
||||||
path = get_icon_path_helper(wrapper->themes, icon_name, size);
|
path = get_icon_path_helper(wrapper->themes, icon_name, size);
|
||||||
if (path) {
|
if (path) {
|
||||||
|
if (debug_icons)
|
||||||
|
fprintf(stderr, "tint2: Icon found: %s\n", path);
|
||||||
add_icon_path_to_cache(wrapper, icon_name, size, path);
|
add_icon_path_to_cache(wrapper, icon_name, size, path);
|
||||||
return path;
|
return path;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!use_fallbacks)
|
if (!use_fallbacks)
|
||||||
goto notfound;
|
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);
|
load_fallbacks(wrapper);
|
||||||
|
|
||||||
path = get_icon_path_helper(wrapper->themes_fallback, icon_name, size);
|
path = get_icon_path_helper(wrapper->themes_fallback, icon_name, size);
|
||||||
@@ -773,7 +798,7 @@ char *get_icon_path(IconThemeWrapper *wrapper, const char *icon_name, int size,
|
|||||||
}
|
}
|
||||||
|
|
||||||
notfound:
|
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);
|
path = get_icon_path_helper(wrapper->themes, DEFAULT_ICON, size);
|
||||||
if (path)
|
if (path)
|
||||||
return 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.
|
// Do not free the result, it is cached.
|
||||||
const GSList *get_icon_locations();
|
const GSList *get_icon_locations();
|
||||||
|
|
||||||
|
extern gboolean debug_icons;
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
+39
-5
@@ -64,6 +64,8 @@ void launcher_reload_hidden_icons(Launcher *launcher);
|
|||||||
void launcher_icon_on_change_layout(void *obj);
|
void launcher_icon_on_change_layout(void *obj);
|
||||||
int launcher_compute_desired_size(void *obj);
|
int launcher_compute_desired_size(void *obj);
|
||||||
|
|
||||||
|
void relayout_launcher();
|
||||||
|
|
||||||
void default_launcher()
|
void default_launcher()
|
||||||
{
|
{
|
||||||
launcher_enabled = 0;
|
launcher_enabled = 0;
|
||||||
@@ -95,6 +97,7 @@ void init_launcher_panel(void *p)
|
|||||||
launcher->area._draw_foreground = NULL;
|
launcher->area._draw_foreground = NULL;
|
||||||
launcher->area.size_mode = LAYOUT_FIXED;
|
launcher->area.size_mode = LAYOUT_FIXED;
|
||||||
launcher->area._resize = resize_launcher;
|
launcher->area._resize = resize_launcher;
|
||||||
|
launcher->area._on_change_layout = relayout_launcher;
|
||||||
launcher->area._compute_desired_size = launcher_compute_desired_size;
|
launcher->area._compute_desired_size = launcher_compute_desired_size;
|
||||||
launcher->area.resize_needed = 1;
|
launcher->area.resize_needed = 1;
|
||||||
schedule_redraw(&launcher->area);
|
schedule_redraw(&launcher->area);
|
||||||
@@ -294,7 +297,7 @@ gboolean resize_launcher(void *obj)
|
|||||||
launcherIcon->y = posy;
|
launcherIcon->y = posy;
|
||||||
launcherIcon->x = posx;
|
launcherIcon->x = posx;
|
||||||
launcher_icon_on_change_layout(launcherIcon);
|
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 (panel_horizontal) {
|
||||||
if (i % icons_per_column) {
|
if (i % icons_per_column) {
|
||||||
posy += launcher->icon_size + launcher->area.paddingx;
|
posy += launcher->icon_size + launcher->area.paddingx;
|
||||||
@@ -325,6 +328,17 @@ gboolean resize_launcher(void *obj)
|
|||||||
return TRUE;
|
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
|
// 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
|
// in a stack; we need to layout them in a kind of table
|
||||||
void launcher_icon_on_change_layout(void *obj)
|
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)
|
void launcher_icon_dump_geometry(void *obj, int indent)
|
||||||
{
|
{
|
||||||
LauncherIcon *launcherIcon = (LauncherIcon *)obj;
|
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)
|
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_icon((Launcher *)icon->area.parent, icon);
|
||||||
launcher_reload_hidden_icons((Launcher *)icon->area.parent);
|
launcher_reload_hidden_icons((Launcher *)icon->area.parent);
|
||||||
|
|
||||||
if (evt->type == ButtonPress || evt->type == ButtonRelease)
|
if (evt->type == ButtonPress || evt->type == ButtonRelease) {
|
||||||
tint_exec(icon->cmd, icon->cwd, icon->icon_tooltip, evt->xbutton.time, &icon->area, x, y);
|
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
|
// Populates the list_icons list from the list_apps list
|
||||||
@@ -476,6 +508,8 @@ void launcher_reload_icon(Launcher *launcher, LauncherIcon *launcherIcon)
|
|||||||
launcherIcon->cwd = strdup(entry.cwd);
|
launcherIcon->cwd = strdup(entry.cwd);
|
||||||
else
|
else
|
||||||
launcherIcon->cwd = NULL;
|
launcherIcon->cwd = NULL;
|
||||||
|
launcherIcon->start_in_terminal = entry.start_in_terminal;
|
||||||
|
launcherIcon->startup_notification = entry.startup_notification;
|
||||||
if (launcherIcon->icon_name)
|
if (launcherIcon->icon_name)
|
||||||
free(launcherIcon->icon_name);
|
free(launcherIcon->icon_name);
|
||||||
launcherIcon->icon_name = entry.icon ? strdup(entry.icon) : strdup(DEFAULT_ICON);
|
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_icon(original);
|
||||||
free(launcherIcon->icon_path);
|
free(launcherIcon->icon_path);
|
||||||
launcherIcon->icon_path = new_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) {
|
if (panel_config.mouse_effects) {
|
||||||
launcherIcon->image_hover = adjust_icon(launcherIcon->image,
|
launcherIcon->image_hover = adjust_icon(launcherIcon->image,
|
||||||
|
|||||||
@@ -33,6 +33,8 @@ typedef struct LauncherIcon {
|
|||||||
Imlib_Image image_pressed;
|
Imlib_Image image_pressed;
|
||||||
char *cmd;
|
char *cmd;
|
||||||
char *cwd;
|
char *cwd;
|
||||||
|
gboolean start_in_terminal;
|
||||||
|
gboolean startup_notification;
|
||||||
char *icon_name;
|
char *icon_name;
|
||||||
char *icon_path;
|
char *icon_path;
|
||||||
char *icon_tooltip;
|
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 ((action == XSETTINGS_ACTION_NEW || action == XSETTINGS_ACTION_CHANGED) && name != NULL && setting != NULL) {
|
||||||
if (strcmp(name, "Net/IconThemeName") == 0 && setting->type == XSETTINGS_TYPE_STRING) {
|
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 (icon_theme_name_xsettings) {
|
||||||
if (strcmp(icon_theme_name_xsettings, setting->data.v_string) == 0)
|
if (strcmp(icon_theme_name_xsettings, setting->data.v_string) == 0)
|
||||||
return;
|
return;
|
||||||
@@ -57,7 +57,7 @@ void xsettings_notify_cb(const char *name, XSettingsAction action, XSettingsSett
|
|||||||
icon_theme_name_xsettings = strdup(setting->data.v_string);
|
icon_theme_name_xsettings = strdup(setting->data.v_string);
|
||||||
default_icon_theme_changed();
|
default_icon_theme_changed();
|
||||||
} else if (strcmp(name, "Gtk/FontName") == 0 && setting->type == XSETTINGS_TYPE_STRING) {
|
} 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 (default_font) {
|
||||||
if (strcmp(default_font, setting->data.v_string) == 0)
|
if (strcmp(default_font, setting->data.v_string) == 0)
|
||||||
return;
|
return;
|
||||||
@@ -191,7 +191,7 @@ static XSettingsList *parse_settings(unsigned char *data, size_t len)
|
|||||||
|
|
||||||
result = fetch_card8(&buffer, (CARD8 *)&buffer.byte_order);
|
result = fetch_card8(&buffer, (CARD8 *)&buffer.byte_order);
|
||||||
if (buffer.byte_order != MSBFirst && buffer.byte_order != LSBFirst) {
|
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;
|
result = XSETTINGS_FAILED;
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
@@ -312,13 +312,13 @@ out:
|
|||||||
if (result != XSETTINGS_SUCCESS) {
|
if (result != XSETTINGS_SUCCESS) {
|
||||||
switch (result) {
|
switch (result) {
|
||||||
case XSETTINGS_NO_MEM:
|
case XSETTINGS_NO_MEM:
|
||||||
fprintf(stderr, "Out of memory reading XSETTINGS property\n");
|
fprintf(stderr, "tint2: Out of memory reading XSETTINGS property\n");
|
||||||
break;
|
break;
|
||||||
case XSETTINGS_ACCESS:
|
case XSETTINGS_ACCESS:
|
||||||
fprintf(stderr, "Invalid XSETTINGS property (read off end)\n");
|
fprintf(stderr, "tint2: Invalid XSETTINGS property (read off end)\n");
|
||||||
break;
|
break;
|
||||||
case XSETTINGS_DUPLICATE_ENTRY:
|
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_FAILED:
|
||||||
case XSETTINGS_SUCCESS:
|
case XSETTINGS_SUCCESS:
|
||||||
case XSETTINGS_NO_ENTRY:
|
case XSETTINGS_NO_ENTRY:
|
||||||
@@ -365,7 +365,7 @@ static void read_settings(XSettingsClient *client)
|
|||||||
|
|
||||||
if (result == Success && type == server.atom._XSETTINGS_SETTINGS) {
|
if (result == Success && type == server.atom._XSETTINGS_SETTINGS) {
|
||||||
if (format != 8) {
|
if (format != 8) {
|
||||||
fprintf(stderr, "Invalid format for XSETTINGS property %d", format);
|
fprintf(stderr, "tint2: Invalid format for XSETTINGS property %d", format);
|
||||||
} else
|
} else
|
||||||
client->settings = parse_settings(data, n_items);
|
client->settings = parse_settings(data, n_items);
|
||||||
XFree(data);
|
XFree(data);
|
||||||
@@ -420,7 +420,7 @@ XSettingsClient *xsettings_client_new(Display *display,
|
|||||||
check_manager_window(client);
|
check_manager_window(client);
|
||||||
|
|
||||||
if (client->manager_window == None) {
|
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);
|
free(client);
|
||||||
return NULL;
|
return NULL;
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
+806
@@ -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];
|
||||||
|
sprintf(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_next_timeout()) >= 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;
|
||||||
|
}
|
||||||
@@ -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);
|
||||||
|
}
|
||||||
@@ -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
|
||||||
+84
-15
@@ -20,6 +20,8 @@
|
|||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <unistd.h>
|
||||||
#include <X11/Xlib.h>
|
#include <X11/Xlib.h>
|
||||||
#include <X11/Xutil.h>
|
#include <X11/Xutil.h>
|
||||||
#include <X11/Xatom.h>
|
#include <X11/Xatom.h>
|
||||||
@@ -36,8 +38,6 @@
|
|||||||
|
|
||||||
void panel_clear_background(void *obj);
|
void panel_clear_background(void *obj);
|
||||||
|
|
||||||
int signal_pending;
|
|
||||||
|
|
||||||
MouseAction mouse_left;
|
MouseAction mouse_left;
|
||||||
MouseAction mouse_middle;
|
MouseAction mouse_middle;
|
||||||
MouseAction mouse_right;
|
MouseAction mouse_right;
|
||||||
@@ -58,6 +58,7 @@ char *panel_window_name = NULL;
|
|||||||
gboolean debug_geometry;
|
gboolean debug_geometry;
|
||||||
gboolean debug_gradients;
|
gboolean debug_gradients;
|
||||||
gboolean startup_notifications;
|
gboolean startup_notifications;
|
||||||
|
gboolean debug_thumbnails;
|
||||||
|
|
||||||
gboolean panel_autohide;
|
gboolean panel_autohide;
|
||||||
int panel_autohide_show_timeout;
|
int panel_autohide_show_timeout;
|
||||||
@@ -148,6 +149,8 @@ void cleanup_panel()
|
|||||||
cleanup_freespace(p);
|
cleanup_freespace(p);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
free_icon_themes();
|
||||||
|
|
||||||
free(panel_items_order);
|
free(panel_items_order);
|
||||||
panel_items_order = NULL;
|
panel_items_order = NULL;
|
||||||
free(panel_window_name);
|
free(panel_window_name);
|
||||||
@@ -175,11 +178,11 @@ void init_panel()
|
|||||||
{
|
{
|
||||||
if (panel_config.monitor > (server.num_monitors - 1)) {
|
if (panel_config.monitor > (server.num_monitors - 1)) {
|
||||||
// server.num_monitors minimum value is 1 (see get_monitors())
|
// 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;
|
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;
|
icon_theme_wrapper = NULL;
|
||||||
|
|
||||||
@@ -207,7 +210,7 @@ void init_panel()
|
|||||||
}
|
}
|
||||||
|
|
||||||
fprintf(stderr,
|
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,
|
server.num_monitors,
|
||||||
num_panels,
|
num_panels,
|
||||||
server.num_desktops);
|
server.num_desktops);
|
||||||
@@ -287,7 +290,7 @@ void init_panel()
|
|||||||
XGCValues gcv;
|
XGCValues gcv;
|
||||||
server.gc = XCreateGC(server.display, p->main_win, 0, &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_properties(p);
|
||||||
set_panel_background(p);
|
set_panel_background(p);
|
||||||
if (!snapshot_path) {
|
if (!snapshot_path) {
|
||||||
@@ -320,7 +323,7 @@ void panel_compute_size(Panel *panel)
|
|||||||
if (panel->fractional_height)
|
if (panel->fractional_height)
|
||||||
panel->area.height = (server.monitors[panel->monitor].height - panel->marginy) * panel->area.height / 100;
|
panel->area.height = (server.monitors[panel->monitor].height - panel->marginy) * panel->area.height / 100;
|
||||||
if (panel->area.bg->border.radius > panel->area.height / 2) {
|
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);
|
g_array_append_val(backgrounds, *panel->area.bg);
|
||||||
panel->area.bg = &g_array_index(backgrounds, Background, backgrounds->len - 1);
|
panel->area.bg = &g_array_index(backgrounds, Background, backgrounds->len - 1);
|
||||||
panel->area.bg->border.radius = panel->area.height / 2;
|
panel->area.bg->border.radius = panel->area.height / 2;
|
||||||
@@ -346,7 +349,7 @@ void panel_compute_size(Panel *panel)
|
|||||||
panel->area.width = old_panel_height;
|
panel->area.width = old_panel_height;
|
||||||
|
|
||||||
if (panel->area.bg->border.radius > panel->area.width / 2) {
|
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);
|
g_array_append_val(backgrounds, *panel->area.bg);
|
||||||
panel->area.bg = &g_array_index(backgrounds, Background, backgrounds->len - 1);
|
panel->area.bg = &g_array_index(backgrounds, Background, backgrounds->len - 1);
|
||||||
panel->area.bg->border.radius = panel->area.width / 2;
|
panel->area.bg->border.radius = panel->area.width / 2;
|
||||||
@@ -399,7 +402,7 @@ void panel_compute_position(Panel *panel)
|
|||||||
panel->hidden_width = panel->area.width - diff;
|
panel->hidden_width = panel->area.width - diff;
|
||||||
panel->hidden_height = panel->area.height;
|
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);
|
// panel->area.height);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -414,7 +417,7 @@ gboolean resize_panel(void *obj)
|
|||||||
Panel *panel = (Panel *)obj;
|
Panel *panel = (Panel *)obj;
|
||||||
relayout_with_constraint(&panel->area, 0);
|
relayout_with_constraint(&panel->area, 0);
|
||||||
|
|
||||||
// printf("resize_panel\n");
|
// fprintf(stderr, "tint2: resize_panel\n");
|
||||||
if (taskbar_mode != MULTI_DESKTOP && taskbar_enabled) {
|
if (taskbar_mode != MULTI_DESKTOP && taskbar_enabled) {
|
||||||
// propagate width/height on hidden taskbar
|
// propagate width/height on hidden taskbar
|
||||||
int width = panel->taskbar[server.desktop].area.width;
|
int width = panel->taskbar[server.desktop].area.width;
|
||||||
@@ -551,7 +554,7 @@ void update_strut(Panel *p)
|
|||||||
long struts[12] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
|
long struts[12] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
|
||||||
if (panel_horizontal) {
|
if (panel_horizontal) {
|
||||||
int height = p->area.height + p->marginy;
|
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;
|
height = p->hidden_height;
|
||||||
if (panel_position & TOP) {
|
if (panel_position & TOP) {
|
||||||
struts[2] = height + monitor.y;
|
struts[2] = height + monitor.y;
|
||||||
@@ -566,7 +569,7 @@ void update_strut(Panel *p)
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
int width = p->area.width + p->marginx;
|
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;
|
width = p->hidden_width;
|
||||||
if (panel_position & LEFT) {
|
if (panel_position & LEFT) {
|
||||||
struts[0] = width + monitor.x;
|
struts[0] = width + monitor.x;
|
||||||
@@ -814,15 +817,24 @@ void set_panel_properties(Panel *p)
|
|||||||
g_free(name);
|
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
|
// Dock
|
||||||
long val = server.atom._NET_WM_WINDOW_TYPE_DOCK;
|
|
||||||
XChangeProperty(server.display,
|
XChangeProperty(server.display,
|
||||||
p->main_win,
|
p->main_win,
|
||||||
server.atom._NET_WM_WINDOW_TYPE,
|
server.atom._NET_WM_WINDOW_TYPE,
|
||||||
XA_ATOM,
|
XA_ATOM,
|
||||||
32,
|
32,
|
||||||
PropModeReplace,
|
PropModeReplace,
|
||||||
(unsigned char *)&val,
|
(unsigned char *)&server.atom._NET_WM_WINDOW_TYPE_DOCK,
|
||||||
1);
|
1);
|
||||||
|
|
||||||
place_panel_all_desktops(p);
|
place_panel_all_desktops(p);
|
||||||
@@ -1135,6 +1147,63 @@ void _schedule_panel_redraw(const char *file, const char *function, const int li
|
|||||||
{
|
{
|
||||||
panel_refresh = TRUE;
|
panel_refresh = TRUE;
|
||||||
if (debug_fps) {
|
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);
|
||||||
|
}
|
||||||
|
|||||||
+6
-2
@@ -29,7 +29,6 @@
|
|||||||
#include "battery.h"
|
#include "battery.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
extern int signal_pending;
|
|
||||||
// --------------------------------------------------
|
// --------------------------------------------------
|
||||||
// mouse events
|
// mouse events
|
||||||
extern MouseAction mouse_left;
|
extern MouseAction mouse_left;
|
||||||
@@ -93,7 +92,9 @@ extern XSettingsClient *xsettings_client;
|
|||||||
extern gboolean startup_notifications;
|
extern gboolean startup_notifications;
|
||||||
extern gboolean debug_geometry;
|
extern gboolean debug_geometry;
|
||||||
extern gboolean debug_fps;
|
extern gboolean debug_fps;
|
||||||
|
extern double tracing_fps_threshold;
|
||||||
extern gboolean debug_frames;
|
extern gboolean debug_frames;
|
||||||
|
extern gboolean debug_thumbnails;
|
||||||
|
|
||||||
typedef struct Panel {
|
typedef struct Panel {
|
||||||
Area area;
|
Area area;
|
||||||
@@ -165,7 +166,7 @@ gboolean resize_panel(void *obj);
|
|||||||
void render_panel(Panel *panel);
|
void render_panel(Panel *panel);
|
||||||
void shrink_panel(Panel *panel);
|
void shrink_panel(Panel *panel);
|
||||||
void _schedule_panel_redraw(const char *file, const char *function, const int line);
|
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 set_panel_items_order(Panel *p);
|
||||||
void place_panel_all_desktops(Panel *p);
|
void place_panel_all_desktops(Panel *p);
|
||||||
@@ -207,4 +208,7 @@ void default_font_changed();
|
|||||||
void free_icon(Imlib_Image icon);
|
void free_icon(Imlib_Image icon);
|
||||||
Imlib_Image scale_icon(Imlib_Image original, int icon_size);
|
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
|
#endif
|
||||||
|
|||||||
+88
-13
@@ -18,15 +18,20 @@
|
|||||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
* 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/Xrender.h>
|
||||||
#include <X11/extensions/Xrandr.h>
|
#include <X11/extensions/Xrandr.h>
|
||||||
|
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
|
#include <fcntl.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
|
|
||||||
#include "server.h"
|
#include "common.h"
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
|
#include "server.h"
|
||||||
|
#include "signals.h"
|
||||||
#include "window.h"
|
#include "window.h"
|
||||||
|
|
||||||
Server server;
|
Server server;
|
||||||
@@ -118,6 +123,14 @@ void server_init_atoms()
|
|||||||
server.atom.TARGETS = XInternAtom(server.display, "TARGETS", False);
|
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()
|
void cleanup_server()
|
||||||
{
|
{
|
||||||
if (server.colormap)
|
if (server.colormap)
|
||||||
@@ -248,7 +261,7 @@ void get_root_pixmap()
|
|||||||
server.root_pmap = ret;
|
server.root_pmap = ret;
|
||||||
|
|
||||||
if (server.root_pmap == None) {
|
if (server.root_pmap == None) {
|
||||||
fprintf(stderr, "tint2 : pixmap background detection failed\n");
|
fprintf(stderr, "tint2: pixmap background detection failed\n");
|
||||||
} else {
|
} else {
|
||||||
XGCValues gcv;
|
XGCValues gcv;
|
||||||
gcv.ts_x_origin = 0;
|
gcv.ts_x_origin = 0;
|
||||||
@@ -308,14 +321,14 @@ void get_monitors()
|
|||||||
|
|
||||||
if (res && res->ncrtc >= num_monitors) {
|
if (res && res->ncrtc >= num_monitors) {
|
||||||
// use xrandr to identify monitors (does not work with proprietery nvidia drivers)
|
// 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));
|
server.monitors = calloc(res->ncrtc, sizeof(Monitor));
|
||||||
num_monitors = 0;
|
num_monitors = 0;
|
||||||
for (int i = 0; i < res->ncrtc; ++i) {
|
for (int i = 0; i < res->ncrtc; ++i) {
|
||||||
XRRCrtcInfo *crtc_info = XRRGetCrtcInfo(server.display, res, res->crtcs[i]);
|
XRRCrtcInfo *crtc_info = XRRGetCrtcInfo(server.display, res, res->crtcs[i]);
|
||||||
// Ignore empty crtc
|
// Ignore empty crtc
|
||||||
if (!crtc_info->width || !crtc_info->height) {
|
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);
|
XRRFreeCrtcInfo(crtc_info);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@@ -328,7 +341,7 @@ void get_monitors()
|
|||||||
server.monitors[i_monitor].names = calloc((crtc_info->noutput + 1), sizeof(gchar *));
|
server.monitors[i_monitor].names = calloc((crtc_info->noutput + 1), sizeof(gchar *));
|
||||||
for (int j = 0; j < crtc_info->noutput; ++j) {
|
for (int j = 0; j < crtc_info->noutput; ++j) {
|
||||||
XRROutputInfo *output_info = XRRGetOutputInfo(server.display, res, crtc_info->outputs[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);
|
server.monitors[i_monitor].names[j] = g_strdup(output_info->name);
|
||||||
XRRFreeOutputInfo(output_info);
|
XRRFreeOutputInfo(output_info);
|
||||||
server.monitors[i_monitor].primary = crtc_info->outputs[j] == primary_output;
|
server.monitors[i_monitor].primary = crtc_info->outputs[j] == primary_output;
|
||||||
@@ -385,7 +398,7 @@ void get_monitors()
|
|||||||
|
|
||||||
void print_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++) {
|
for (int i = 0; i < server.num_monitors; i++) {
|
||||||
fprintf(stderr,
|
fprintf(stderr,
|
||||||
"Monitor %d: x = %d, y = %d, w = %d, h = %d\n",
|
"Monitor %d: x = %d, y = %d, w = %d, h = %d\n",
|
||||||
@@ -507,10 +520,10 @@ int get_current_desktop()
|
|||||||
|
|
||||||
int ncols = x_screen_width / work_area_width;
|
int ncols = x_screen_width / work_area_width;
|
||||||
|
|
||||||
// fprintf(stderr, "\n");
|
// fprintf(stderr, "tint2: \n");
|
||||||
// fprintf(stderr, "Work area size: %d x %d\n", work_area_width, work_area_height);
|
// fprintf(stderr, "tint2: 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, "tint2: 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: 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;
|
int result = (viewport_y / work_area_height) * ncols + viewport_x / work_area_width;
|
||||||
return MAX(0, MIN(server.num_desktops - 1, result));
|
return MAX(0, MIN(server.num_desktops - 1, result));
|
||||||
@@ -541,7 +554,7 @@ void get_desktops()
|
|||||||
}
|
}
|
||||||
if (server.num_desktops == 0) {
|
if (server.num_desktops == 0) {
|
||||||
server.num_desktops = 1;
|
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");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -585,15 +598,77 @@ void server_init_visual()
|
|||||||
|
|
||||||
server.real_transparency = TRUE;
|
server.real_transparency = TRUE;
|
||||||
server.depth = 32;
|
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.colormap = XCreateColormap(server.display, server.root_win, visual, AllocNone);
|
||||||
server.visual = visual;
|
server.visual = visual;
|
||||||
} else {
|
} else {
|
||||||
// no composite manager or snapshot mode => fake transparency
|
// no composite manager or snapshot mode => fake transparency
|
||||||
server.real_transparency = FALSE;
|
server.real_transparency = FALSE;
|
||||||
server.depth = DefaultDepth(server.display, server.screen);
|
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.colormap = DefaultColormap(server.display, server.screen);
|
||||||
server.visual = DefaultVisual(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;
|
Atom TARGETS;
|
||||||
} Global_atom;
|
} 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 {
|
typedef struct Monitor {
|
||||||
int x;
|
int x;
|
||||||
int y;
|
int y;
|
||||||
@@ -111,6 +120,7 @@ typedef struct Viewport {
|
|||||||
|
|
||||||
typedef struct Server {
|
typedef struct Server {
|
||||||
Display *display;
|
Display *display;
|
||||||
|
int x11_fd;
|
||||||
Window root_win;
|
Window root_win;
|
||||||
Window composite_manager;
|
Window composite_manager;
|
||||||
gboolean real_transparency;
|
gboolean real_transparency;
|
||||||
@@ -135,6 +145,9 @@ typedef struct Server {
|
|||||||
Colormap colormap;
|
Colormap colormap;
|
||||||
Colormap colormap32;
|
Colormap colormap32;
|
||||||
Global_atom atom;
|
Global_atom atom;
|
||||||
|
int xdamage_event_type;
|
||||||
|
int xdamage_event_error_type;
|
||||||
|
gboolean has_shm;
|
||||||
#ifdef HAVE_SN
|
#ifdef HAVE_SN
|
||||||
SnDisplay *sn_display;
|
SnDisplay *sn_display;
|
||||||
GTree *pids;
|
GTree *pids;
|
||||||
@@ -153,6 +166,10 @@ Atom server_get_atom(char *atom_name);
|
|||||||
void server_catch_error(Display *d, XErrorEvent *ev);
|
void server_catch_error(Display *d, XErrorEvent *ev);
|
||||||
void server_init_atoms();
|
void server_init_atoms();
|
||||||
void server_init_visual();
|
void server_init_visual();
|
||||||
|
void server_init_xdamage();
|
||||||
|
|
||||||
|
void x11_io_error(Display *display);
|
||||||
|
void handle_crash(const char *reason);
|
||||||
|
|
||||||
// detect root background
|
// detect root background
|
||||||
void get_root_pixmap();
|
void get_root_pixmap();
|
||||||
@@ -167,4 +184,12 @@ GSList *get_desktop_names();
|
|||||||
int get_current_desktop();
|
int get_current_desktop();
|
||||||
void change_desktop(int 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
|
#endif
|
||||||
|
|||||||
+166
@@ -0,0 +1,166 @@
|
|||||||
|
#include <errno.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <glib.h>
|
||||||
|
#ifdef HAVE_SN
|
||||||
|
#include <libsn/sn.h>
|
||||||
|
#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);
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
@@ -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
|
||||||
+64
-113
@@ -114,10 +114,10 @@ void init_systray()
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
systray_composited = !server.disable_transparency && server.visual32 && server.colormap32;
|
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) {
|
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.alpha = 100;
|
||||||
systray.brightness = systray.saturation = 0;
|
systray.brightness = systray.saturation = 0;
|
||||||
}
|
}
|
||||||
@@ -184,7 +184,7 @@ int systray_compute_desired_size(void *obj)
|
|||||||
gboolean resize_systray(void *obj)
|
gboolean resize_systray(void *obj)
|
||||||
{
|
{
|
||||||
if (systray_profile)
|
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;
|
int size;
|
||||||
systray_compute_geometry(&size);
|
systray_compute_geometry(&size);
|
||||||
@@ -227,7 +227,7 @@ gboolean resize_systray(void *obj)
|
|||||||
void draw_systray(void *obj, cairo_t *c)
|
void draw_systray(void *obj, cairo_t *c)
|
||||||
{
|
{
|
||||||
if (systray_profile)
|
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 (systray_composited) {
|
||||||
if (render_background)
|
if (render_background)
|
||||||
XFreePixmap(server.display, render_background);
|
XFreePixmap(server.display, render_background);
|
||||||
@@ -252,12 +252,12 @@ void systray_dump_geometry(void *obj, int indent)
|
|||||||
{
|
{
|
||||||
Systray *tray = (Systray *)obj;
|
Systray *tray = (Systray *)obj;
|
||||||
|
|
||||||
fprintf(stderr, "%*sIcons:\n", indent, "");
|
fprintf(stderr, "tint2: %*sIcons:\n", indent, "");
|
||||||
indent += 2;
|
indent += 2;
|
||||||
for (GSList *l = tray->list_icons; l; l = l->next) {
|
for (GSList *l = tray->list_icons; l; l = l->next) {
|
||||||
TrayWindow *traywin = (TrayWindow *)l->data;
|
TrayWindow *traywin = (TrayWindow *)l->data;
|
||||||
fprintf(stderr,
|
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,
|
indent,
|
||||||
"",
|
"",
|
||||||
traywin->x,
|
traywin->x,
|
||||||
@@ -271,7 +271,7 @@ void systray_dump_geometry(void *obj, int indent)
|
|||||||
void on_change_systray(void *obj)
|
void on_change_systray(void *obj)
|
||||||
{
|
{
|
||||||
if (systray_profile)
|
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)
|
if (systray.icons_per_column == 0 || systray.icons_per_row == 0)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
@@ -301,7 +301,7 @@ void on_change_systray(void *obj)
|
|||||||
if (systray_profile)
|
if (systray_profile)
|
||||||
fprintf(stderr,
|
fprintf(stderr,
|
||||||
"%s:%d win = %lu (%s), parent = %lu, x = %d, y = %d\n",
|
"%s:%d win = %lu (%s), parent = %lu, x = %d, y = %d\n",
|
||||||
__FUNCTION__,
|
__func__,
|
||||||
__LINE__,
|
__LINE__,
|
||||||
traywin->win,
|
traywin->win,
|
||||||
traywin->name,
|
traywin->name,
|
||||||
@@ -332,7 +332,7 @@ void on_change_systray(void *obj)
|
|||||||
unsigned int width, height, depth;
|
unsigned int width, height, depth;
|
||||||
Window root;
|
Window root;
|
||||||
if (!XGetGeometry(server.display, traywin->parent, &root, &xpos, &ypos, &width, &height, &border_width, &depth)) {
|
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 (width != traywin->width || height != traywin->height || xpos != traywin->x || ypos != traywin->y) {
|
||||||
if (systray_profile)
|
if (systray_profile)
|
||||||
@@ -358,7 +358,7 @@ void on_change_systray(void *obj)
|
|||||||
void start_net()
|
void start_net()
|
||||||
{
|
{
|
||||||
if (systray_profile)
|
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) {
|
if (net_sel_win) {
|
||||||
// protocol already started
|
// protocol already started
|
||||||
if (!systray_enabled)
|
if (!systray_enabled)
|
||||||
@@ -395,11 +395,11 @@ void start_net()
|
|||||||
&bytes_after,
|
&bytes_after,
|
||||||
&prop);
|
&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) {
|
if (ret == Success && prop) {
|
||||||
pid = prop[1] * 256;
|
pid = prop[1] * 256;
|
||||||
pid += prop[0];
|
pid += prop[0];
|
||||||
fprintf(stderr, " pid=%d", pid);
|
fprintf(stderr, "tint2: pid=%d", pid);
|
||||||
}
|
}
|
||||||
fprintf(stderr, RESET "\n");
|
fprintf(stderr, RESET "\n");
|
||||||
return;
|
return;
|
||||||
@@ -407,7 +407,7 @@ void start_net()
|
|||||||
|
|
||||||
// init systray protocol
|
// init systray protocol
|
||||||
net_sel_win = XCreateSimpleWindow(server.display, server.root_win, -1, -1, 1, 1, 0, 0, 0);
|
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.
|
// v0.3 trayer specification. tint2 always horizontal.
|
||||||
// Vertical panel will draw the systray 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);
|
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) {
|
if (XGetSelectionOwner(server.display, server.atom._NET_SYSTEM_TRAY_SCREEN) != net_sel_win) {
|
||||||
stop_net();
|
stop_net();
|
||||||
fprintf(stderr, RED "tint2 : can't get systray manager" RESET "\n");
|
fprintf(stderr, RED "tint2: cannot find systray manager" RESET "\n");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
fprintf(stderr, GREEN "tint2 : systray started" RESET "\n");
|
fprintf(stderr, GREEN "tint2: systray started" RESET "\n");
|
||||||
if (systray_profile)
|
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;
|
XClientMessageEvent ev;
|
||||||
ev.type = ClientMessage;
|
ev.type = ClientMessage;
|
||||||
ev.window = server.root_win;
|
ev.window = server.root_win;
|
||||||
@@ -478,10 +478,10 @@ void start_net()
|
|||||||
XSendEvent(server.display, server.root_win, False, StructureNotifyMask, (XEvent *)&ev);
|
XSendEvent(server.display, server.root_win, False, StructureNotifyMask, (XEvent *)&ev);
|
||||||
}
|
}
|
||||||
|
|
||||||
void net_message(XClientMessageEvent *e)
|
void handle_systray_event(XClientMessageEvent *e)
|
||||||
{
|
{
|
||||||
if (systray_profile)
|
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;
|
Window win;
|
||||||
unsigned long opcode = e->data.l[1];
|
unsigned long opcode = e->data.l[1];
|
||||||
@@ -499,9 +499,9 @@ void net_message(XClientMessageEvent *e)
|
|||||||
|
|
||||||
default:
|
default:
|
||||||
if (opcode == server.atom._NET_SYSTEM_TRAY_MESSAGE_DATA)
|
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
|
else
|
||||||
fprintf(stderr, RED "SYSTEM_TRAY : unknown message type" RESET "\n");
|
fprintf(stderr, RED "tint2: SYSTEM_TRAY : unknown message type" RESET "\n");
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -509,7 +509,7 @@ void net_message(XClientMessageEvent *e)
|
|||||||
void stop_net()
|
void stop_net()
|
||||||
{
|
{
|
||||||
if (systray_profile)
|
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) {
|
if (systray.list_icons) {
|
||||||
// remove_icon change systray.list_icons
|
// remove_icon change systray.list_icons
|
||||||
while (systray.list_icons)
|
while (systray.list_icons)
|
||||||
@@ -529,10 +529,10 @@ gboolean error;
|
|||||||
int window_error_handler(Display *d, XErrorEvent *e)
|
int window_error_handler(Display *d, XErrorEvent *e)
|
||||||
{
|
{
|
||||||
if (systray_profile)
|
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;
|
error = TRUE;
|
||||||
if (e->error_code != BadWindow) {
|
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;
|
return 0;
|
||||||
}
|
}
|
||||||
@@ -564,18 +564,18 @@ static gint compare_traywindows(gconstpointer a, gconstpointer b)
|
|||||||
|
|
||||||
void print_icons()
|
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) {
|
for (GSList *l = systray.list_icons; l; l = l->next) {
|
||||||
TrayWindow *t = l->data;
|
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) {
|
for (GSList *l = systray.list_icons; l; l = l->next) {
|
||||||
if (l->next) {
|
if (l->next) {
|
||||||
TrayWindow *t = l->data;
|
TrayWindow *t = l->data;
|
||||||
TrayWindow *u = l->next->data;
|
TrayWindow *u = l->next->data;
|
||||||
int cmp = compare_traywindows(t, u);
|
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) {
|
if (!systray_hide_name_regex) {
|
||||||
systray_hide_name_regex = (regex_t *)calloc(1, sizeof(*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) {
|
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);
|
free_and_null(systray_hide_name_regex);
|
||||||
return FALSE;
|
return FALSE;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
char *name = get_window_name(win);
|
char *name = get_window_name(win);
|
||||||
if (regexec(systray_hide_name_regex, name, 0, NULL, 0) == 0) {
|
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;
|
return TRUE;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -623,7 +623,7 @@ gboolean add_icon(Window win)
|
|||||||
|
|
||||||
char *name = get_window_name(win);
|
char *name = get_window_name(win);
|
||||||
if (systray_profile)
|
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;
|
Panel *panel = systray.area.panel;
|
||||||
|
|
||||||
// Get the process ID of the application that created the window
|
// 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
|
// Create the parent window that will embed the icon
|
||||||
XWindowAttributes attr;
|
XWindowAttributes attr;
|
||||||
if (systray_profile)
|
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) {
|
if (XGetWindowAttributes(server.display, win, &attr) == False) {
|
||||||
free(name);
|
free(name);
|
||||||
XSelectInput(server.display, win, NoEventMask);
|
XSelectInput(server.display, win, NoEventMask);
|
||||||
@@ -679,7 +679,7 @@ gboolean add_icon(Window win)
|
|||||||
win,
|
win,
|
||||||
name,
|
name,
|
||||||
pid,
|
pid,
|
||||||
attr.visual,
|
(void*)attr.visual,
|
||||||
attr.colormap,
|
attr.colormap,
|
||||||
attr.depth,
|
attr.depth,
|
||||||
attr.width,
|
attr.width,
|
||||||
@@ -706,7 +706,7 @@ gboolean add_icon(Window win)
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (systray_profile)
|
if (systray_profile)
|
||||||
fprintf(stderr, "XCreateWindow(...)\n");
|
fprintf(stderr, "tint2: XCreateWindow(...)\n");
|
||||||
Window parent = XCreateWindow(server.display,
|
Window parent = XCreateWindow(server.display,
|
||||||
panel->main_win,
|
panel->main_win,
|
||||||
0,
|
0,
|
||||||
@@ -743,19 +743,19 @@ gboolean add_icon(Window win)
|
|||||||
|
|
||||||
if (!panel->is_hidden) {
|
if (!panel->is_hidden) {
|
||||||
if (systray_profile)
|
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);
|
XMapRaised(server.display, traywin->parent);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (systray_profile)
|
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
|
// Resize and redraw the systray
|
||||||
if (systray_profile)
|
if (systray_profile)
|
||||||
fprintf(stderr,
|
fprintf(stderr,
|
||||||
BLUE "[%f] %s:%d trigger resize & redraw" RESET "\n",
|
BLUE "[%f] %s:%d trigger resize & redraw" RESET "\n",
|
||||||
profiling_get_time(),
|
profiling_get_time(),
|
||||||
__FUNCTION__,
|
__func__,
|
||||||
__LINE__);
|
__LINE__);
|
||||||
systray.area.resize_needed = TRUE;
|
systray.area.resize_needed = TRUE;
|
||||||
panel->area.resize_needed = TRUE;
|
panel->area.resize_needed = TRUE;
|
||||||
@@ -770,7 +770,7 @@ gboolean reparent_icon(TrayWindow *traywin)
|
|||||||
fprintf(stderr,
|
fprintf(stderr,
|
||||||
"[%f] %s:%d win = %lu (%s)\n",
|
"[%f] %s:%d win = %lu (%s)\n",
|
||||||
profiling_get_time(),
|
profiling_get_time(),
|
||||||
__FUNCTION__,
|
__func__,
|
||||||
__LINE__,
|
__LINE__,
|
||||||
traywin->win,
|
traywin->win,
|
||||||
traywin->name);
|
traywin->name);
|
||||||
@@ -808,7 +808,7 @@ gboolean reparent_icon(TrayWindow *traywin)
|
|||||||
e.xclient.data.l[3] = traywin->parent;
|
e.xclient.data.l[3] = traywin->parent;
|
||||||
e.xclient.data.l[4] = 0;
|
e.xclient.data.l[4] = 0;
|
||||||
if (systray_profile)
|
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);
|
XSendEvent(server.display, traywin->win, False, NoEventMask, &e);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -832,7 +832,7 @@ gboolean reparent_icon(TrayWindow *traywin)
|
|||||||
fprintf(stderr,
|
fprintf(stderr,
|
||||||
"[%f] %s:%d win = %lu (%s)\n",
|
"[%f] %s:%d win = %lu (%s)\n",
|
||||||
profiling_get_time(),
|
profiling_get_time(),
|
||||||
__FUNCTION__,
|
__func__,
|
||||||
__LINE__,
|
__LINE__,
|
||||||
traywin->win,
|
traywin->win,
|
||||||
traywin->name);
|
traywin->name);
|
||||||
@@ -846,7 +846,7 @@ gboolean embed_icon(TrayWindow *traywin)
|
|||||||
fprintf(stderr,
|
fprintf(stderr,
|
||||||
"[%f] %s:%d win = %lu (%s)\n",
|
"[%f] %s:%d win = %lu (%s)\n",
|
||||||
profiling_get_time(),
|
profiling_get_time(),
|
||||||
__FUNCTION__,
|
__func__,
|
||||||
__LINE__,
|
__LINE__,
|
||||||
traywin->win,
|
traywin->win,
|
||||||
traywin->name);
|
traywin->name);
|
||||||
@@ -859,62 +859,13 @@ gboolean embed_icon(TrayWindow *traywin)
|
|||||||
error = FALSE;
|
error = FALSE;
|
||||||
XErrorHandler old = XSetErrorHandler(window_error_handler);
|
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
|
// Redirect rendering when using compositing
|
||||||
if (systray_composited) {
|
if (systray_composited) {
|
||||||
if (systray_profile)
|
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);
|
traywin->damage = XDamageCreate(server.display, traywin->parent, XDamageReportRawRectangles);
|
||||||
if (systray_profile)
|
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);
|
XCompositeRedirectWindow(server.display, traywin->parent, CompositeRedirectManual);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -922,16 +873,16 @@ gboolean embed_icon(TrayWindow *traywin)
|
|||||||
|
|
||||||
// Make the icon visible
|
// Make the icon visible
|
||||||
if (systray_profile)
|
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);
|
XMapWindow(server.display, traywin->win);
|
||||||
if (!panel->is_hidden) {
|
if (!panel->is_hidden) {
|
||||||
if (systray_profile)
|
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);
|
XMapRaised(server.display, traywin->parent);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (systray_profile)
|
if (systray_profile)
|
||||||
fprintf(stderr, "XSync(server.display, False)\n");
|
fprintf(stderr, "tint2: XSync(server.display, False)\n");
|
||||||
XSync(server.display, False);
|
XSync(server.display, False);
|
||||||
XSetErrorHandler(old);
|
XSetErrorHandler(old);
|
||||||
if (error != FALSE) {
|
if (error != FALSE) {
|
||||||
@@ -952,7 +903,7 @@ gboolean embed_icon(TrayWindow *traywin)
|
|||||||
fprintf(stderr,
|
fprintf(stderr,
|
||||||
"[%f] %s:%d win = %lu (%s)\n",
|
"[%f] %s:%d win = %lu (%s)\n",
|
||||||
profiling_get_time(),
|
profiling_get_time(),
|
||||||
__FUNCTION__,
|
__func__,
|
||||||
__LINE__,
|
__LINE__,
|
||||||
traywin->win,
|
traywin->win,
|
||||||
traywin->name);
|
traywin->name);
|
||||||
@@ -966,7 +917,7 @@ void remove_icon(TrayWindow *traywin)
|
|||||||
fprintf(stderr,
|
fprintf(stderr,
|
||||||
"[%f] %s:%d win = %lu (%s)\n",
|
"[%f] %s:%d win = %lu (%s)\n",
|
||||||
profiling_get_time(),
|
profiling_get_time(),
|
||||||
__FUNCTION__,
|
__func__,
|
||||||
__LINE__,
|
__LINE__,
|
||||||
traywin->win,
|
traywin->win,
|
||||||
traywin->name);
|
traywin->name);
|
||||||
@@ -974,7 +925,7 @@ void remove_icon(TrayWindow *traywin)
|
|||||||
|
|
||||||
// remove from our list
|
// remove from our list
|
||||||
systray.list_icons = g_slist_remove(systray.list_icons, traywin);
|
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);
|
XSelectInput(server.display, traywin->win, NoEventMask);
|
||||||
if (traywin->damage)
|
if (traywin->damage)
|
||||||
@@ -1012,7 +963,7 @@ void remove_icon(TrayWindow *traywin)
|
|||||||
fprintf(stderr,
|
fprintf(stderr,
|
||||||
BLUE "[%f] %s:%d trigger resize & redraw" RESET "\n",
|
BLUE "[%f] %s:%d trigger resize & redraw" RESET "\n",
|
||||||
profiling_get_time(),
|
profiling_get_time(),
|
||||||
__FUNCTION__,
|
__func__,
|
||||||
__LINE__);
|
__LINE__);
|
||||||
systray.area.resize_needed = TRUE;
|
systray.area.resize_needed = TRUE;
|
||||||
panel->area.resize_needed = TRUE;
|
panel->area.resize_needed = TRUE;
|
||||||
@@ -1135,7 +1086,7 @@ void systray_reconfigure_event(TrayWindow *traywin, XEvent *e)
|
|||||||
fprintf(stderr,
|
fprintf(stderr,
|
||||||
BLUE "[%f] %s:%d trigger resize & redraw" RESET "\n",
|
BLUE "[%f] %s:%d trigger resize & redraw" RESET "\n",
|
||||||
profiling_get_time(),
|
profiling_get_time(),
|
||||||
__FUNCTION__,
|
__func__,
|
||||||
__LINE__);
|
__LINE__);
|
||||||
schedule_panel_redraw();
|
schedule_panel_redraw();
|
||||||
refresh_systray = TRUE;
|
refresh_systray = TRUE;
|
||||||
@@ -1214,7 +1165,7 @@ void systray_resize_request_event(TrayWindow *traywin, XEvent *e)
|
|||||||
fprintf(stderr,
|
fprintf(stderr,
|
||||||
BLUE "[%f] %s:%d trigger resize & redraw" RESET "\n",
|
BLUE "[%f] %s:%d trigger resize & redraw" RESET "\n",
|
||||||
profiling_get_time(),
|
profiling_get_time(),
|
||||||
__FUNCTION__,
|
__func__,
|
||||||
__LINE__);
|
__LINE__);
|
||||||
schedule_panel_redraw();
|
schedule_panel_redraw();
|
||||||
refresh_systray = TRUE;
|
refresh_systray = TRUE;
|
||||||
@@ -1226,7 +1177,7 @@ void systray_destroy_event(TrayWindow *traywin)
|
|||||||
fprintf(stderr,
|
fprintf(stderr,
|
||||||
"[%f] %s:%d win = %lu (%s)\n",
|
"[%f] %s:%d win = %lu (%s)\n",
|
||||||
profiling_get_time(),
|
profiling_get_time(),
|
||||||
__FUNCTION__,
|
__func__,
|
||||||
__LINE__,
|
__LINE__,
|
||||||
traywin->win,
|
traywin->win,
|
||||||
traywin->name);
|
traywin->name);
|
||||||
@@ -1261,7 +1212,7 @@ void systray_render_icon_composited(void *t)
|
|||||||
fprintf(stderr,
|
fprintf(stderr,
|
||||||
"[%f] %s:%d win = %lu (%s)\n",
|
"[%f] %s:%d win = %lu (%s)\n",
|
||||||
profiling_get_time(),
|
profiling_get_time(),
|
||||||
__FUNCTION__,
|
__func__,
|
||||||
__LINE__,
|
__LINE__,
|
||||||
traywin->win,
|
traywin->win,
|
||||||
traywin->name);
|
traywin->name);
|
||||||
@@ -1279,7 +1230,7 @@ void systray_render_icon_composited(void *t)
|
|||||||
fprintf(stderr,
|
fprintf(stderr,
|
||||||
YELLOW "[%f] %s:%d win = %lu (%s) delaying rendering" RESET "\n",
|
YELLOW "[%f] %s:%d win = %lu (%s) delaying rendering" RESET "\n",
|
||||||
profiling_get_time(),
|
profiling_get_time(),
|
||||||
__FUNCTION__,
|
__func__,
|
||||||
__LINE__,
|
__LINE__,
|
||||||
traywin->win,
|
traywin->win,
|
||||||
traywin->name);
|
traywin->name);
|
||||||
@@ -1299,7 +1250,7 @@ void systray_render_icon_composited(void *t)
|
|||||||
fprintf(stderr,
|
fprintf(stderr,
|
||||||
YELLOW "[%f] %s:%d win = %lu (%s) delaying rendering" RESET "\n",
|
YELLOW "[%f] %s:%d win = %lu (%s) delaying rendering" RESET "\n",
|
||||||
profiling_get_time(),
|
profiling_get_time(),
|
||||||
__FUNCTION__,
|
__func__,
|
||||||
__LINE__,
|
__LINE__,
|
||||||
traywin->win,
|
traywin->win,
|
||||||
traywin->name);
|
traywin->name);
|
||||||
@@ -1328,7 +1279,7 @@ void systray_render_icon_composited(void *t)
|
|||||||
} else if (traywin->depth == 32) {
|
} else if (traywin->depth == 32) {
|
||||||
f = XRenderFindStandardFormat(server.display, PictStandardARGB32);
|
f = XRenderFindStandardFormat(server.display, PictStandardARGB32);
|
||||||
} else {
|
} 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);
|
XFreePixmap(server.display, tmp_pmap);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -1430,7 +1381,7 @@ void systray_render_icon_composited(void *t)
|
|||||||
fprintf(stderr,
|
fprintf(stderr,
|
||||||
"[%f] %s:%d win = %lu (%s)\n",
|
"[%f] %s:%d win = %lu (%s)\n",
|
||||||
profiling_get_time(),
|
profiling_get_time(),
|
||||||
__FUNCTION__,
|
__func__,
|
||||||
__LINE__,
|
__LINE__,
|
||||||
traywin->win,
|
traywin->win,
|
||||||
traywin->name);
|
traywin->name);
|
||||||
@@ -1468,7 +1419,7 @@ void systray_render_icon(void *t)
|
|||||||
// fprintf(stderr,
|
// fprintf(stderr,
|
||||||
// YELLOW "[%f] %s:%d win = %lu (%s) delaying rendering" RESET "\n",
|
// YELLOW "[%f] %s:%d win = %lu (%s) delaying rendering" RESET "\n",
|
||||||
// profiling_get_time(),
|
// profiling_get_time(),
|
||||||
// __FUNCTION__,
|
// __func__,
|
||||||
// __LINE__,
|
// __LINE__,
|
||||||
// traywin->win,
|
// traywin->win,
|
||||||
// traywin->name);
|
// traywin->name);
|
||||||
@@ -1482,7 +1433,7 @@ void systray_render_icon(void *t)
|
|||||||
fprintf(stderr,
|
fprintf(stderr,
|
||||||
"[%f] %s:%d win = %lu (%s)\n",
|
"[%f] %s:%d win = %lu (%s)\n",
|
||||||
profiling_get_time(),
|
profiling_get_time(),
|
||||||
__FUNCTION__,
|
__func__,
|
||||||
__LINE__,
|
__LINE__,
|
||||||
traywin->win,
|
traywin->win,
|
||||||
traywin->name);
|
traywin->name);
|
||||||
@@ -1498,7 +1449,7 @@ void systray_render_icon(void *t)
|
|||||||
Window root;
|
Window root;
|
||||||
if (!XGetGeometry(server.display, traywin->win, &root, &xpos, &ypos, &width, &height, &border_width, &depth)) {
|
if (!XGetGeometry(server.display, traywin->win, &root, &xpos, &ypos, &width, &height, &border_width, &depth)) {
|
||||||
stop_timeout(traywin->render_timeout);
|
stop_timeout(traywin->render_timeout);
|
||||||
if (!traywin->resize_timeout)
|
if (!traywin->render_timeout)
|
||||||
traywin->render_timeout =
|
traywin->render_timeout =
|
||||||
add_timeout(min_refresh_period, 0, systray_render_icon, traywin, &traywin->render_timeout);
|
add_timeout(min_refresh_period, 0, systray_render_icon, traywin, &traywin->render_timeout);
|
||||||
systray_render_icon_from_image(traywin);
|
systray_render_icon_from_image(traywin);
|
||||||
@@ -1507,7 +1458,7 @@ void systray_render_icon(void *t)
|
|||||||
} else {
|
} else {
|
||||||
if (xpos != 0 || ypos != 0 || width != traywin->width || height != traywin->height) {
|
if (xpos != 0 || ypos != 0 || width != traywin->width || height != traywin->height) {
|
||||||
stop_timeout(traywin->render_timeout);
|
stop_timeout(traywin->render_timeout);
|
||||||
if (!traywin->resize_timeout)
|
if (!traywin->render_timeout)
|
||||||
traywin->render_timeout =
|
traywin->render_timeout =
|
||||||
add_timeout(min_refresh_period, 0, systray_render_icon, traywin, &traywin->render_timeout);
|
add_timeout(min_refresh_period, 0, systray_render_icon, traywin, &traywin->render_timeout);
|
||||||
systray_render_icon_from_image(traywin);
|
systray_render_icon_from_image(traywin);
|
||||||
@@ -1515,7 +1466,7 @@ void systray_render_icon(void *t)
|
|||||||
fprintf(stderr,
|
fprintf(stderr,
|
||||||
YELLOW "[%f] %s:%d win = %lu (%s) delaying rendering" RESET "\n",
|
YELLOW "[%f] %s:%d win = %lu (%s) delaying rendering" RESET "\n",
|
||||||
profiling_get_time(),
|
profiling_get_time(),
|
||||||
__FUNCTION__,
|
__func__,
|
||||||
__LINE__,
|
__LINE__,
|
||||||
traywin->win,
|
traywin->win,
|
||||||
traywin->name);
|
traywin->name);
|
||||||
@@ -1527,7 +1478,7 @@ void systray_render_icon(void *t)
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (systray_profile)
|
if (systray_profile)
|
||||||
fprintf(stderr, "rendering tray icon\n");
|
fprintf(stderr, "tint2: rendering tray icon\n");
|
||||||
|
|
||||||
if (systray_composited) {
|
if (systray_composited) {
|
||||||
systray_render_icon_composited(traywin);
|
systray_render_icon_composited(traywin);
|
||||||
@@ -1549,7 +1500,7 @@ void systray_render_icon(void *t)
|
|||||||
void refresh_systray_icons()
|
void refresh_systray_icons()
|
||||||
{
|
{
|
||||||
if (systray_profile)
|
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;
|
TrayWindow *traywin;
|
||||||
GSList *l;
|
GSList *l;
|
||||||
for (l = systray.list_icons; l; l = l->next) {
|
for (l = systray.list_icons; l; l = l->next) {
|
||||||
|
|||||||
@@ -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
|
// many tray icon doesn't manage stop/restart of the systray manager
|
||||||
void start_net();
|
void start_net();
|
||||||
void stop_net();
|
void stop_net();
|
||||||
void net_message(XClientMessageEvent *e);
|
void handle_systray_event(XClientMessageEvent *e);
|
||||||
|
|
||||||
gboolean add_icon(Window id);
|
gboolean add_icon(Window id);
|
||||||
gboolean reparent_icon(TrayWindow *traywin);
|
gboolean reparent_icon(TrayWindow *traywin);
|
||||||
|
|||||||
+205
-54
@@ -40,6 +40,8 @@ GSList *urgent_list;
|
|||||||
|
|
||||||
void task_dump_geometry(void *obj, int indent);
|
void task_dump_geometry(void *obj, int indent);
|
||||||
int task_compute_desired_size(void *obj);
|
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)
|
char *task_get_tooltip(void *obj)
|
||||||
{
|
{
|
||||||
@@ -47,6 +49,17 @@ char *task_get_tooltip(void *obj)
|
|||||||
return strdup(t->title);
|
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)
|
Task *add_task(Window win)
|
||||||
{
|
{
|
||||||
if (!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.has_mouse_press_effect = panel_config.mouse_effects;
|
||||||
task_template.area._dump_geometry = task_dump_geometry;
|
task_template.area._dump_geometry = task_dump_geometry;
|
||||||
task_template.area._is_under_mouse = full_width_area_is_under_mouse;
|
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.win = win;
|
||||||
task_template.desktop = get_window_desktop(win);
|
task_template.desktop = get_window_desktop(win);
|
||||||
task_template.area.panel = &panels[monitor];
|
task_template.area.panel = &panels[monitor];
|
||||||
@@ -93,10 +107,6 @@ Task *add_task(Window win)
|
|||||||
(int)win,
|
(int)win,
|
||||||
task_template.title ? task_template.title : "null");
|
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();
|
GPtrArray *task_buttons = g_ptr_array_new();
|
||||||
for (int j = 0; j < panels[monitor].num_desktops; j++) {
|
for (int j = 0; j < panels[monitor].num_desktops; j++) {
|
||||||
if (task_template.desktop != ALL_DESKTOPS && task_template.desktop != 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._dump_geometry = task_dump_geometry;
|
||||||
task_instance->area._is_under_mouse = full_width_area_is_under_mouse;
|
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._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->win = task_template.win;
|
||||||
task_instance->desktop = task_template.desktop;
|
task_instance->desktop = task_template.desktop;
|
||||||
task_instance->win_x = task_template.win_x;
|
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->win_h = task_template.win_h;
|
||||||
task_instance->current_state = TASK_UNDEFINED; // to update the current state later in set_task_state...
|
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) {
|
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->area.on_screen = always_show_all_desktop_tasks;
|
||||||
}
|
}
|
||||||
task_instance->title = task_template.title;
|
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_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) {
|
for (int k = 0; k < TASK_STATE_COUNT; ++k) {
|
||||||
task_instance->icon[k] = task_template.icon[k];
|
task_instance->icon[k] = task_template.icon[k];
|
||||||
task_instance->icon_hover[k] = task_template.icon_hover[k];
|
task_instance->icon_hover[k] = task_template.icon_hover[k];
|
||||||
@@ -186,9 +201,6 @@ void remove_task(Task *task)
|
|||||||
if (!task)
|
if (!task)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
// fprintf(stderr, "%s %d: win = %ld, task = %s\n", __FUNCTION__, __LINE__, task->win, task->title ? task->title :
|
|
||||||
// "??");
|
|
||||||
|
|
||||||
if (taskbar_mode == MULTI_DESKTOP) {
|
if (taskbar_mode == MULTI_DESKTOP) {
|
||||||
Panel *panel = task->area.panel;
|
Panel *panel = task->area.panel;
|
||||||
panel->area.resize_needed = 1;
|
panel->area.resize_needed = 1;
|
||||||
@@ -198,9 +210,10 @@ void remove_task(Task *task)
|
|||||||
|
|
||||||
// free title and icon just for the first task
|
// free title and icon just for the first task
|
||||||
// even with task_on_all_desktop and with task_on_all_panel
|
// 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)
|
if (task->title)
|
||||||
free(task->title);
|
free(task->title);
|
||||||
|
if (task->thumbnail)
|
||||||
|
cairo_surface_destroy(task->thumbnail);
|
||||||
task_remove_icon(task);
|
task_remove_icon(task);
|
||||||
|
|
||||||
GPtrArray *task_buttons = g_hash_table_lookup(win_to_task, &win);
|
GPtrArray *task_buttons = g_hash_table_lookup(win_to_task, &win);
|
||||||
@@ -212,6 +225,8 @@ void remove_task(Task *task)
|
|||||||
task_drag = 0;
|
task_drag = 0;
|
||||||
if (g_slist_find(urgent_list, task2))
|
if (g_slist_find(urgent_list, task2))
|
||||||
del_urgent(task2);
|
del_urgent(task2);
|
||||||
|
if (g_tooltip.area == &task2->area)
|
||||||
|
tooltip_hide(NULL);
|
||||||
remove_area((Area *)task2);
|
remove_area((Area *)task2);
|
||||||
free(task2);
|
free(task2);
|
||||||
}
|
}
|
||||||
@@ -266,36 +281,23 @@ gboolean task_update_title(Task *task)
|
|||||||
return TRUE;
|
return TRUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
void task_update_icon(Task *task)
|
Imlib_Image task_get_icon(Window win, int icon_size)
|
||||||
{
|
{
|
||||||
Panel *panel = task->area.panel;
|
|
||||||
if (!panel->g_task.has_icon)
|
|
||||||
return;
|
|
||||||
|
|
||||||
task_remove_icon(task);
|
|
||||||
|
|
||||||
Imlib_Image img = NULL;
|
Imlib_Image img = NULL;
|
||||||
|
|
||||||
if (!img) {
|
if (!img) {
|
||||||
int len;
|
int len;
|
||||||
gulong *data = server_get_property(task->win, server.atom._NET_WM_ICON, XA_CARDINAL, &len);
|
gulong *data = server_get_property(win, server.atom._NET_WM_ICON, XA_CARDINAL, &len);
|
||||||
if (data) {
|
if (data) {
|
||||||
if (len > 0) {
|
if (len > 0) {
|
||||||
// get ARGB icon
|
// get ARGB icon
|
||||||
int w, h;
|
int w, h;
|
||||||
gulong *tmp_data = get_best_icon(data, get_icon_count(data, len), len, &w, &h, panel->g_task.icon_size1);
|
gulong *tmp_data = get_best_icon(data, get_icon_count(data, len), len, &w, &h, icon_size);
|
||||||
if (tmp_data) {
|
if (tmp_data) {
|
||||||
DATA32 icon_data[w * h];
|
DATA32 icon_data[w * h];
|
||||||
for (int j = 0; j < w * h; ++j)
|
for (int j = 0; j < w * h; ++j)
|
||||||
icon_data[j] = tmp_data[j];
|
icon_data[j] = tmp_data[j];
|
||||||
img = imlib_create_image_using_copied_data(w, h, icon_data);
|
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");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
XFree(data);
|
XFree(data);
|
||||||
@@ -303,7 +305,7 @@ void task_update_icon(Task *task)
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!img) {
|
if (!img) {
|
||||||
XWMHints *hints = XGetWMHints(server.display, task->win);
|
XWMHints *hints = XGetWMHints(server.display, win);
|
||||||
if (hints) {
|
if (hints) {
|
||||||
if (hints->flags & IconPixmapHint && hints->icon_pixmap != 0) {
|
if (hints->flags & IconPixmapHint && hints->icon_pixmap != 0) {
|
||||||
// get width, height and depth for the pixmap
|
// get width, height and depth for the pixmap
|
||||||
@@ -315,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);
|
XGetGeometry(server.display, hints->icon_pixmap, &root, &icon_x, &icon_y, &w, &h, &border_width, &bpp);
|
||||||
imlib_context_set_drawable(hints->icon_pixmap);
|
imlib_context_set_drawable(hints->icon_pixmap);
|
||||||
img = imlib_create_image_from_drawable(hints->icon_mask, 0, 0, w, h, 0);
|
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);
|
XFree(hints);
|
||||||
}
|
}
|
||||||
@@ -330,10 +325,46 @@ void task_update_icon(Task *task)
|
|||||||
if (img == NULL) {
|
if (img == NULL) {
|
||||||
imlib_context_set_image(default_icon);
|
imlib_context_set_image(default_icon);
|
||||||
img = imlib_clone_image();
|
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
|
// transform icons
|
||||||
imlib_context_set_image(img);
|
imlib_context_set_image(img);
|
||||||
imlib_image_set_has_alpha(1);
|
imlib_image_set_has_alpha(1);
|
||||||
@@ -347,20 +378,10 @@ void task_update_icon(Task *task)
|
|||||||
task->icon_width = imlib_image_get_width();
|
task->icon_width = imlib_image_get_width();
|
||||||
task->icon_height = imlib_image_get_height();
|
task->icon_height = imlib_image_get_height();
|
||||||
for (int k = 0; k < TASK_STATE_COUNT; ++k) {
|
for (int k = 0; k < TASK_STATE_COUNT; ++k) {
|
||||||
imlib_context_set_image(orig_image);
|
task->icon[k] = adjust_icon(orig_image,
|
||||||
task->icon[k] = imlib_clone_image();
|
panel->g_task.alpha[k],
|
||||||
imlib_context_set_image(task->icon[k]);
|
panel->g_task.saturation[k],
|
||||||
DATA32 *data32;
|
panel->g_task.brightness[k] != 0);
|
||||||
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);
|
|
||||||
}
|
|
||||||
if (panel_config.mouse_effects) {
|
if (panel_config.mouse_effects) {
|
||||||
task->icon_hover[k] = adjust_icon(task->icon[k],
|
task->icon_hover[k] = adjust_icon(task->icon[k],
|
||||||
panel_config.mouse_over_alpha,
|
panel_config.mouse_over_alpha,
|
||||||
@@ -378,9 +399,12 @@ void task_update_icon(Task *task)
|
|||||||
GPtrArray *task_buttons = get_task_buttons(task->win);
|
GPtrArray *task_buttons = get_task_buttons(task->win);
|
||||||
if (task_buttons) {
|
if (task_buttons) {
|
||||||
for (int i = 0; i < task_buttons->len; ++i) {
|
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_width = task->icon_width;
|
||||||
task2->icon_height = task->icon_height;
|
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) {
|
for (int k = 0; k < TASK_STATE_COUNT; ++k) {
|
||||||
task2->icon[k] = task->icon[k];
|
task2->icon[k] = task->icon[k];
|
||||||
task2->icon_hover[k] = task->icon_hover[k];
|
task2->icon_hover[k] = task->icon_hover[k];
|
||||||
@@ -468,7 +492,7 @@ void task_dump_geometry(void *obj, int indent)
|
|||||||
Panel *panel = (Panel *)task->area.panel;
|
Panel *panel = (Panel *)task->area.panel;
|
||||||
|
|
||||||
fprintf(stderr,
|
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,
|
indent,
|
||||||
"",
|
"",
|
||||||
(int)panel->g_task.text_posx,
|
(int)panel->g_task.text_posx,
|
||||||
@@ -478,7 +502,7 @@ void task_dump_geometry(void *obj, int indent)
|
|||||||
panel->g_task.centered ? "center" : "left",
|
panel->g_task.centered ? "center" : "left",
|
||||||
task->title);
|
task->title);
|
||||||
fprintf(stderr,
|
fprintf(stderr,
|
||||||
"%*sIcon: x = %d, y = %d, w = h = %d\n",
|
"tint2: %*sIcon: x = %d, y = %d, w = h = %d\n",
|
||||||
indent,
|
indent,
|
||||||
"",
|
"",
|
||||||
task->_icon_x,
|
task->_icon_x,
|
||||||
@@ -486,6 +510,24 @@ void task_dump_geometry(void *obj, int indent)
|
|||||||
panel->g_task.icon_size1);
|
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)
|
int task_compute_desired_size(void *obj)
|
||||||
{
|
{
|
||||||
Task *task = (Task *)obj;
|
Task *task = (Task *)obj;
|
||||||
@@ -593,7 +635,6 @@ void reset_active_task()
|
|||||||
}
|
}
|
||||||
|
|
||||||
Window w1 = get_active_window();
|
Window w1 = get_active_window();
|
||||||
// printf("Change active task %ld\n", w1);
|
|
||||||
|
|
||||||
if (w1) {
|
if (w1) {
|
||||||
if (!get_task_buttons(w1)) {
|
if (!get_task_buttons(w1)) {
|
||||||
@@ -605,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)
|
void set_task_state(Task *task, TaskState state)
|
||||||
{
|
{
|
||||||
if (!task || state == TASK_UNDEFINED || state >= TASK_STATE_COUNT)
|
if (!task || state == TASK_UNDEFINED || state >= TASK_STATE_COUNT)
|
||||||
return;
|
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) {
|
if (state == TASK_ACTIVE && task->current_state != state) {
|
||||||
clock_gettime(CLOCK_MONOTONIC, &task->last_activation_time);
|
clock_gettime(CLOCK_MONOTONIC, &task->last_activation_time);
|
||||||
if (taskbar_sort_method == TASKBAR_SORT_LRU || taskbar_sort_method == TASKBAR_SORT_MRU) {
|
if (taskbar_sort_method == TASKBAR_SORT_LRU || taskbar_sort_method == TASKBAR_SORT_MRU) {
|
||||||
@@ -719,3 +798,75 @@ void del_urgent(Task *task)
|
|||||||
urgent_timeout = NULL;
|
urgent_timeout = NULL;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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 <X11/Xlib.h>
|
||||||
#include <pango/pangocairo.h>
|
#include <pango/pangocairo.h>
|
||||||
#include <Imlib2.h>
|
#include <Imlib2.h>
|
||||||
|
|
||||||
#include "common.h"
|
#include "common.h"
|
||||||
#include "timer.h"
|
#include "timer.h"
|
||||||
|
|
||||||
@@ -41,10 +42,13 @@ typedef struct GlobalTask {
|
|||||||
// starting position for text ~ task_padding + task_border + icon_size
|
// starting position for text ~ task_padding + task_border + icon_size
|
||||||
double text_posx, text_height;
|
double text_posx, text_height;
|
||||||
gboolean has_font;
|
gboolean has_font;
|
||||||
|
gboolean has_content_tint;
|
||||||
PangoFontDescription *font_desc;
|
PangoFontDescription *font_desc;
|
||||||
Color font[TASK_STATE_COUNT];
|
Color font[TASK_STATE_COUNT];
|
||||||
int config_font_mask;
|
int config_font_mask;
|
||||||
gboolean tooltip_enabled;
|
gboolean tooltip_enabled;
|
||||||
|
gboolean thumbnail_enabled;
|
||||||
|
int thumbnail_width;
|
||||||
} GlobalTask;
|
} GlobalTask;
|
||||||
|
|
||||||
// Stores information about a task.
|
// Stores information about a task.
|
||||||
@@ -60,6 +64,9 @@ typedef struct Task {
|
|||||||
Imlib_Image icon_press[TASK_STATE_COUNT];
|
Imlib_Image icon_press[TASK_STATE_COUNT];
|
||||||
unsigned int icon_width;
|
unsigned int icon_width;
|
||||||
unsigned int icon_height;
|
unsigned int icon_height;
|
||||||
|
Color icon_color;
|
||||||
|
Color icon_color_hover;
|
||||||
|
Color icon_color_press;
|
||||||
char *title;
|
char *title;
|
||||||
int urgent_tick;
|
int urgent_tick;
|
||||||
// These may not be up-to-date
|
// These may not be up-to-date
|
||||||
@@ -73,6 +80,8 @@ typedef struct Task {
|
|||||||
double _text_posy;
|
double _text_posy;
|
||||||
int _icon_x;
|
int _icon_x;
|
||||||
int _icon_y;
|
int _icon_y;
|
||||||
|
cairo_surface_t *thumbnail;
|
||||||
|
double thumbnail_last_update;
|
||||||
} Task;
|
} Task;
|
||||||
|
|
||||||
extern timeout *urgent_timeout;
|
extern timeout *urgent_timeout;
|
||||||
@@ -85,9 +94,12 @@ void draw_task(void *obj, cairo_t *c);
|
|||||||
void on_change_task(void *obj);
|
void on_change_task(void *obj);
|
||||||
|
|
||||||
void task_update_icon(Task *task);
|
void task_update_icon(Task *task);
|
||||||
|
void task_update_desktop(Task *task);
|
||||||
gboolean task_update_title(Task *task);
|
gboolean task_update_title(Task *task);
|
||||||
void reset_active_task();
|
void reset_active_task();
|
||||||
void set_task_state(Task *task, TaskState state);
|
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),
|
// 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.
|
// returns a pointer to the Task for the active window on the same taskbar.
|
||||||
|
|||||||
+176
-17
@@ -32,6 +32,7 @@
|
|||||||
#include "window.h"
|
#include "window.h"
|
||||||
#include "panel.h"
|
#include "panel.h"
|
||||||
#include "strnatcmp.h"
|
#include "strnatcmp.h"
|
||||||
|
#include "tooltip.h"
|
||||||
|
|
||||||
GHashTable *win_to_task;
|
GHashTable *win_to_task;
|
||||||
|
|
||||||
@@ -46,6 +47,12 @@ gboolean hide_taskbar_if_empty;
|
|||||||
gboolean always_show_all_desktop_tasks;
|
gboolean always_show_all_desktop_tasks;
|
||||||
TaskbarSortMethod taskbar_sort_method;
|
TaskbarSortMethod taskbar_sort_method;
|
||||||
Alignment taskbar_alignment;
|
Alignment taskbar_alignment;
|
||||||
|
static timeout *thumbnail_update_timer_all;
|
||||||
|
static timeout *thumbnail_update_timer_active;
|
||||||
|
static timeout *thumbnail_update_timer_tooltip;
|
||||||
|
|
||||||
|
static GList *taskbar_task_orderings = NULL;
|
||||||
|
static GList *taskbar_thumbnail_jobs_done = NULL;
|
||||||
|
|
||||||
void taskbar_init_fonts();
|
void taskbar_init_fonts();
|
||||||
int taskbar_compute_desired_size(void *obj);
|
int taskbar_compute_desired_size(void *obj);
|
||||||
@@ -53,6 +60,8 @@ int taskbar_compute_desired_size(void *obj);
|
|||||||
// Removes the task with &win = key. The other args are ignored.
|
// Removes the task with &win = key. The other args are ignored.
|
||||||
void taskbar_remove_task(Window *win);
|
void taskbar_remove_task(Window *win);
|
||||||
|
|
||||||
|
void taskbar_update_thumbnails(void *arg);
|
||||||
|
|
||||||
guint win_hash(gconstpointer key)
|
guint win_hash(gconstpointer key)
|
||||||
{
|
{
|
||||||
return *((const Window *)key);
|
return *((const Window *)key);
|
||||||
@@ -80,13 +89,56 @@ void default_taskbar()
|
|||||||
hide_task_diff_monitor = FALSE;
|
hide_task_diff_monitor = FALSE;
|
||||||
hide_taskbar_if_empty = FALSE;
|
hide_taskbar_if_empty = FALSE;
|
||||||
always_show_all_desktop_tasks = FALSE;
|
always_show_all_desktop_tasks = FALSE;
|
||||||
|
thumbnail_update_timer_all = NULL;
|
||||||
|
thumbnail_update_timer_active = NULL;
|
||||||
|
thumbnail_update_timer_tooltip = NULL;
|
||||||
|
taskbar_thumbnail_jobs_done = NULL;
|
||||||
taskbar_sort_method = TASKBAR_NOSORT;
|
taskbar_sort_method = TASKBAR_NOSORT;
|
||||||
taskbar_alignment = ALIGN_LEFT;
|
taskbar_alignment = ALIGN_LEFT;
|
||||||
default_taskbarname();
|
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()
|
void cleanup_taskbar()
|
||||||
{
|
{
|
||||||
|
stop_timeout(thumbnail_update_timer_all);
|
||||||
|
stop_timeout(thumbnail_update_timer_active);
|
||||||
|
stop_timeout(thumbnail_update_timer_tooltip);
|
||||||
|
g_list_free(taskbar_thumbnail_jobs_done);
|
||||||
|
taskbar_save_orderings();
|
||||||
if (win_to_task) {
|
if (win_to_task) {
|
||||||
while (g_hash_table_size(win_to_task)) {
|
while (g_hash_table_size(win_to_task)) {
|
||||||
GHashTableIter iter;
|
GHashTableIter iter;
|
||||||
@@ -136,6 +188,9 @@ void init_taskbar()
|
|||||||
panel_config.g_task.has_text = panel_config.g_task.has_icon = 1;
|
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)
|
if (!win_to_task)
|
||||||
win_to_task = g_hash_table_new_full(win_hash, win_compare, free, free_ptr_array);
|
win_to_task = g_hash_table_new_full(win_hash, win_compare, free, free_ptr_array);
|
||||||
|
|
||||||
@@ -267,8 +322,9 @@ void init_taskbar_panel(void *p)
|
|||||||
if (!panel->g_task.background[j])
|
if (!panel->g_task.background[j])
|
||||||
panel->g_task.background[j] = &g_array_index(backgrounds, Background, 0);
|
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) {
|
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",
|
fprintf(stderr,
|
||||||
j == 0 ? "_" : j == 1 ? "_active_" : j == 2 ? "_iconified_" : "_urgent_");
|
"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]);
|
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] = &g_array_index(backgrounds, Background, backgrounds->len - 1);
|
||||||
panel->g_task.background[j]->border.radius = panel->g_task.area.height / 2;
|
panel->g_task.background[j]->border.radius = panel->g_task.area.height / 2;
|
||||||
@@ -299,16 +355,6 @@ void init_taskbar_panel(void *p)
|
|||||||
top_bottom_border_width(&panel->g_task.area));
|
top_bottom_border_width(&panel->g_task.area));
|
||||||
panel->g_task.text_posx += panel->g_task.icon_size1 + panel->g_task.area.paddingx;
|
panel->g_task.text_posx += panel->g_task.icon_size1 + panel->g_task.area.paddingx;
|
||||||
panel->g_task.icon_posy = (panel->g_task.area.height - panel->g_task.icon_size1) / 2;
|
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;
|
Taskbar *taskbar;
|
||||||
@@ -329,6 +375,21 @@ void init_taskbar_panel(void *p)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
init_taskbarname_panel(panel);
|
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_timeout(mode == THUMB_MODE_ALL ? &thumbnail_update_timer_all :
|
||||||
|
mode == THUMB_MODE_ACTIVE_WINDOW ? &thumbnail_update_timer_active : &thumbnail_update_timer_tooltip,
|
||||||
|
mode == THUMB_MODE_TOOLTIP_WINDOW ? 1000 : 500,
|
||||||
|
mode == THUMB_MODE_ALL ? 10 * 1000 : 0,
|
||||||
|
taskbar_update_thumbnails,
|
||||||
|
(void *)(long)mode);
|
||||||
}
|
}
|
||||||
|
|
||||||
void taskbar_init_fonts()
|
void taskbar_init_fonts()
|
||||||
@@ -391,14 +452,67 @@ GPtrArray *get_task_buttons(Window win)
|
|||||||
return NULL;
|
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()
|
void taskbar_refresh_tasklist()
|
||||||
{
|
{
|
||||||
if (!taskbar_enabled)
|
if (!taskbar_enabled)
|
||||||
return;
|
return;
|
||||||
// fprintf(stderr, "%s %d:\n", __FUNCTION__, __LINE__);
|
|
||||||
|
|
||||||
int num_results;
|
int num_results;
|
||||||
Window *win = server_get_property(server.root_win, server.atom._NET_CLIENT_LIST, XA_WINDOW, &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)
|
if (!win)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
@@ -406,7 +520,7 @@ void taskbar_refresh_tasklist()
|
|||||||
for (GList *it = win_list; it; it = it->next) {
|
for (GList *it = win_list; it; it = it->next) {
|
||||||
int i;
|
int i;
|
||||||
for (i = 0; i < num_results; i++)
|
for (i = 0; i < num_results; i++)
|
||||||
if (*((Window *)it->data) == win[i])
|
if (*((Window *)it->data) == sorted[i])
|
||||||
break;
|
break;
|
||||||
if (i == num_results)
|
if (i == num_results)
|
||||||
taskbar_remove_task(it->data);
|
taskbar_remove_task(it->data);
|
||||||
@@ -415,10 +529,11 @@ void taskbar_refresh_tasklist()
|
|||||||
|
|
||||||
// Add any new
|
// Add any new
|
||||||
for (int i = 0; i < num_results; i++)
|
for (int i = 0; i < num_results; i++)
|
||||||
if (!get_task(win[i]))
|
if (!get_task(sorted[i]))
|
||||||
add_task(win[i]);
|
add_task(sorted[i]);
|
||||||
|
|
||||||
XFree(win);
|
XFree(win);
|
||||||
|
free(sorted);
|
||||||
}
|
}
|
||||||
|
|
||||||
int taskbar_compute_desired_size(void *obj)
|
int taskbar_compute_desired_size(void *obj)
|
||||||
@@ -442,7 +557,6 @@ gboolean resize_taskbar(void *obj)
|
|||||||
Taskbar *taskbar = (Taskbar *)obj;
|
Taskbar *taskbar = (Taskbar *)obj;
|
||||||
Panel *panel = (Panel *)taskbar->area.panel;
|
Panel *panel = (Panel *)taskbar->area.panel;
|
||||||
|
|
||||||
// printf("resize_taskbar %d %d\n", taskbar->area.posx, taskbar->area.posy);
|
|
||||||
if (panel_horizontal) {
|
if (panel_horizontal) {
|
||||||
relayout_with_constraint(&taskbar->area, panel->g_task.maximum_width);
|
relayout_with_constraint(&taskbar->area, panel->g_task.maximum_width);
|
||||||
|
|
||||||
@@ -700,3 +814,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_timeout(&thumbnail_update_timer_all, 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_timeout(&thumbnail_update_timer_all, 10 * 1000, 10 * 1000, taskbar_update_thumbnails, arg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -25,6 +25,12 @@ typedef enum TaskbarSortMethod {
|
|||||||
TASKBAR_SORT_MRU,
|
TASKBAR_SORT_MRU,
|
||||||
} TaskbarSortMethod;
|
} TaskbarSortMethod;
|
||||||
|
|
||||||
|
typedef enum ThumbnailUpdateMode {
|
||||||
|
THUMB_MODE_ACTIVE_WINDOW = 0,
|
||||||
|
THUMB_MODE_TOOLTIP_WINDOW,
|
||||||
|
THUMB_MODE_ALL
|
||||||
|
} ThumbnailUpdateMode;
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
Area area;
|
Area area;
|
||||||
gchar *name;
|
gchar *name;
|
||||||
@@ -72,6 +78,7 @@ void init_taskbar_panel(void *p);
|
|||||||
|
|
||||||
gboolean resize_taskbar(void *obj);
|
gboolean resize_taskbar(void *obj);
|
||||||
void taskbar_default_font_changed();
|
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.
|
// Reloads the entire list of tasks from the window manager and recreates the task buttons.
|
||||||
void taskbar_refresh_tasklist();
|
void taskbar_refresh_tasklist();
|
||||||
|
|||||||
@@ -214,3 +214,35 @@ void draw_taskbarname(void *obj, cairo_t *c)
|
|||||||
|
|
||||||
g_object_unref(layout);
|
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 taskbarname_default_font_changed();
|
||||||
|
|
||||||
|
void update_desktop_names();
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
-2196
File diff suppressed because it is too large
Load Diff
@@ -22,6 +22,7 @@ include_directories( ../util
|
|||||||
set(SOURCES ../util/common.c
|
set(SOURCES ../util/common.c
|
||||||
../util/strnatcmp.c
|
../util/strnatcmp.c
|
||||||
../util/cache.c
|
../util/cache.c
|
||||||
|
../util/timer.c
|
||||||
../config.c
|
../config.c
|
||||||
../server.c
|
../server.c
|
||||||
../launcher/apps-common.c
|
../launcher/apps-common.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_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_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_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)
|
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_BOOL,
|
||||||
GTK_TYPE_BOOL);
|
GTK_TYPE_BOOL,
|
||||||
|
GTK_TYPE_DOUBLE,
|
||||||
|
GTK_TYPE_DOUBLE);
|
||||||
|
|
||||||
GtkWidget *table, *label, *button;
|
GtkWidget *table, *label, *button;
|
||||||
int row, col;
|
int row, col;
|
||||||
@@ -175,6 +177,19 @@ void create_background(GtkWidget *parent)
|
|||||||
col++;
|
col++;
|
||||||
gtk_tooltips_set_tip(tooltips, background_fill_color, _("The fill color of the current background"), NULL);
|
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;
|
row++, col = 2;
|
||||||
label = gtk_label_new(_("Border color"));
|
label = gtk_label_new(_("Border color"));
|
||||||
gtk_misc_set_alignment(GTK_MISC(label), 0, 0);
|
gtk_misc_set_alignment(GTK_MISC(label), 0, 0);
|
||||||
@@ -189,6 +204,19 @@ void create_background(GtkWidget *parent)
|
|||||||
col++;
|
col++;
|
||||||
gtk_tooltips_set_tip(tooltips, background_border_color, _("The border color of the current background"), NULL);
|
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;
|
row++, col = 2;
|
||||||
label = gtk_label_new(_("Gradient"));
|
label = gtk_label_new(_("Gradient"));
|
||||||
gtk_misc_set_alignment(GTK_MISC(label), 0, 0);
|
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_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_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_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);
|
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));
|
r = gtk_spin_button_get_value(GTK_SPIN_BUTTON(background_corner_radius));
|
||||||
b = gtk_spin_button_get_value(GTK_SPIN_BUTTON(background_border_width));
|
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 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 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));
|
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,
|
sideLeft,
|
||||||
bgColBorderSidesRight,
|
bgColBorderSidesRight,
|
||||||
sideRight,
|
sideRight,
|
||||||
|
bgColFillWeight,
|
||||||
|
fill_weight,
|
||||||
|
bgColBorderWeight,
|
||||||
|
border_weight,
|
||||||
-1);
|
-1);
|
||||||
background_update_image(index);
|
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_left, index > 0);
|
||||||
gtk_widget_set_sensitive(background_border_sides_right, 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_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;
|
background_updates_disabled = TRUE;
|
||||||
|
|
||||||
@@ -875,6 +914,9 @@ void current_background_changed(GtkWidget *widget, gpointer data)
|
|||||||
int r;
|
int r;
|
||||||
int b;
|
int b;
|
||||||
|
|
||||||
|
double fill_weight;
|
||||||
|
double border_weight;
|
||||||
|
|
||||||
gboolean sideTop;
|
gboolean sideTop;
|
||||||
gboolean sideBottom;
|
gboolean sideBottom;
|
||||||
gboolean sideLeft;
|
gboolean sideLeft;
|
||||||
@@ -938,6 +980,10 @@ void current_background_changed(GtkWidget *widget, gpointer data)
|
|||||||
&sideLeft,
|
&sideLeft,
|
||||||
bgColBorderSidesRight,
|
bgColBorderSidesRight,
|
||||||
&sideRight,
|
&sideRight,
|
||||||
|
bgColFillWeight,
|
||||||
|
&fill_weight,
|
||||||
|
bgColBorderWeight,
|
||||||
|
&border_weight,
|
||||||
-1);
|
-1);
|
||||||
|
|
||||||
gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(background_border_sides_top), sideTop);
|
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_border_width), b);
|
||||||
gtk_spin_button_set_value(GTK_SPIN_BUTTON(background_corner_radius), r);
|
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, fillColor);
|
||||||
g_boxed_free(GDK_TYPE_COLOR, borderColor);
|
g_boxed_free(GDK_TYPE_COLOR, borderColor);
|
||||||
g_boxed_free(GDK_TYPE_COLOR, fillColorOver);
|
g_boxed_free(GDK_TYPE_COLOR, fillColorOver);
|
||||||
|
|||||||
@@ -227,7 +227,7 @@ int main(int argc, char **argv)
|
|||||||
{
|
{
|
||||||
gchar *tint2_config_dir = g_build_filename(g_get_user_config_dir(), "tint2", NULL);
|
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))
|
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);
|
g_free(tint2_config_dir);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+268
-239
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
+268
-257
File diff suppressed because it is too large
Load Diff
+268
-239
File diff suppressed because it is too large
Load Diff
+268
-239
File diff suppressed because it is too large
Load Diff
+268
-239
File diff suppressed because it is too large
Load Diff
+268
-239
File diff suppressed because it is too large
Load Diff
+266
-240
File diff suppressed because it is too large
Load Diff
@@ -15,3 +15,22 @@ do
|
|||||||
cat ${lang}.pox > ${lang}.po
|
cat ${lang}.pox > ${lang}.po
|
||||||
rm ${lang}.pox
|
rm ${lang}.pox
|
||||||
done
|
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
|
||||||
|
|||||||
@@ -97,7 +97,7 @@ GtkWidget *systray_background, *systray_monitor, *systray_name_filter;
|
|||||||
|
|
||||||
// tooltip
|
// tooltip
|
||||||
GtkWidget *tooltip_padding_x, *tooltip_padding_y, *tooltip_font, *tooltip_font_set, *tooltip_font_color;
|
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 *clock_format_tooltip, *clock_tmz_tooltip;
|
||||||
GtkWidget *tooltip_background;
|
GtkWidget *tooltip_background;
|
||||||
|
|
||||||
@@ -1839,7 +1839,7 @@ void load_desktop_file(const char *file, gboolean selected)
|
|||||||
if (pixbuf)
|
if (pixbuf)
|
||||||
g_object_unref(pixbuf);
|
g_object_unref(pixbuf);
|
||||||
} else {
|
} else {
|
||||||
printf("Could not load %s\n", file);
|
fprintf(stderr, "tint2: Could not load %s\n", file);
|
||||||
GdkPixbuf *pixbuf = load_icon(DEFAULT_ICON);
|
GdkPixbuf *pixbuf = load_icon(DEFAULT_ICON);
|
||||||
GtkTreeIter iter;
|
GtkTreeIter iter;
|
||||||
gtk_list_store_append(store, &iter);
|
gtk_list_store_append(store, &iter);
|
||||||
@@ -1897,7 +1897,7 @@ void load_desktop_entry(const char *file, GList **entries)
|
|||||||
|
|
||||||
DesktopEntry *entry = calloc(1, sizeof(DesktopEntry));
|
DesktopEntry *entry = calloc(1, sizeof(DesktopEntry));
|
||||||
if (!read_desktop_file(file, entry))
|
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) {
|
if (entry->hidden_from_menus) {
|
||||||
free(entry);
|
free(entry);
|
||||||
return;
|
return;
|
||||||
@@ -2480,7 +2480,7 @@ void create_launcher(GtkWidget *parent, GtkWindow *window)
|
|||||||
|
|
||||||
change_paragraph(parent);
|
change_paragraph(parent);
|
||||||
|
|
||||||
fprintf(stderr, "Loading icon themes\n");
|
fprintf(stderr, "tint2: Loading icon themes\n");
|
||||||
GList *themes = NULL;
|
GList *themes = NULL;
|
||||||
const GSList *location;
|
const GSList *location;
|
||||||
for (location = get_icon_locations(); location; location = g_slist_next(location)) {
|
for (location = get_icon_locations(); location; location = g_slist_next(location)) {
|
||||||
@@ -2509,9 +2509,9 @@ void create_launcher(GtkWidget *parent, GtkWindow *window)
|
|||||||
free_icon_theme((IconTheme *)l->data);
|
free_icon_theme((IconTheme *)l->data);
|
||||||
}
|
}
|
||||||
g_list_free(themes);
|
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;
|
GList *entries = NULL;
|
||||||
for (location = get_apps_locations(); location; location = g_slist_next(location)) {
|
for (location = get_apps_locations(); location; location = g_slist_next(location)) {
|
||||||
const gchar *path = (gchar *)location->data;
|
const gchar *path = (gchar *)location->data;
|
||||||
@@ -2528,7 +2528,7 @@ void create_launcher(GtkWidget *parent, GtkWindow *window)
|
|||||||
icon_theme_changed(window);
|
icon_theme_changed(window);
|
||||||
load_icons(launcher_apps);
|
load_icons(launcher_apps);
|
||||||
load_icons(all_apps);
|
load_icons(all_apps);
|
||||||
fprintf(stderr, "Desktop files loaded\n");
|
fprintf(stderr, "tint2: Desktop files loaded\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
void create_taskbar(GtkWidget *parent)
|
void create_taskbar(GtkWidget *parent)
|
||||||
@@ -3288,6 +3288,36 @@ void create_task(GtkWidget *parent)
|
|||||||
"over task buttons."),
|
"over task buttons."),
|
||||||
NULL);
|
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;
|
row++, col = 2;
|
||||||
label = gtk_label_new(_("Maximum width"));
|
label = gtk_label_new(_("Maximum width"));
|
||||||
gtk_misc_set_alignment(GTK_MISC(label), 0, 0);
|
gtk_misc_set_alignment(GTK_MISC(label), 0, 0);
|
||||||
@@ -5136,8 +5166,8 @@ void create_systemtray(GtkWidget *parent)
|
|||||||
_("Specifies the order used to arrange the system tray icons. \n"
|
_("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"
|
"'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"
|
"'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"
|
"'Left to right' means that icons are always added to the right. \n"
|
||||||
"'Right to left' means that icons are always added to the right."),
|
"'Right to left' means that icons are always added to the left."),
|
||||||
NULL);
|
NULL);
|
||||||
|
|
||||||
row++;
|
row++;
|
||||||
|
|||||||
@@ -105,7 +105,7 @@ extern GtkWidget *systray_background, *systray_monitor, *systray_name_filter;
|
|||||||
|
|
||||||
// tooltip
|
// tooltip
|
||||||
extern GtkWidget *tooltip_padding_x, *tooltip_padding_y, *tooltip_font, *tooltip_font_set, *tooltip_font_color;
|
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 *clock_format_tooltip, *clock_tmz_tooltip;
|
||||||
extern GtkWidget *tooltip_background;
|
extern GtkWidget *tooltip_background;
|
||||||
|
|
||||||
@@ -203,6 +203,8 @@ enum {
|
|||||||
bgColBorderSidesBottom,
|
bgColBorderSidesBottom,
|
||||||
bgColBorderSidesLeft,
|
bgColBorderSidesLeft,
|
||||||
bgColBorderSidesRight,
|
bgColBorderSidesRight,
|
||||||
|
bgColFillWeight,
|
||||||
|
bgColBorderWeight,
|
||||||
bgNumCols
|
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_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_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_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
|
// gradients
|
||||||
enum { grColPixbuf = 0, grColId, grColText, grNumCols };
|
enum { grColPixbuf = 0, grColId, grColText, grNumCols };
|
||||||
|
|||||||
@@ -161,6 +161,8 @@ void config_write_backgrounds(FILE *fp)
|
|||||||
|
|
||||||
int r;
|
int r;
|
||||||
int b;
|
int b;
|
||||||
|
double fill_weight;
|
||||||
|
double border_weight;
|
||||||
gboolean sideTop;
|
gboolean sideTop;
|
||||||
gboolean sideBottom;
|
gboolean sideBottom;
|
||||||
gboolean sideLeft;
|
gboolean sideLeft;
|
||||||
@@ -228,6 +230,10 @@ void config_write_backgrounds(FILE *fp)
|
|||||||
&sideLeft,
|
&sideLeft,
|
||||||
bgColBorderSidesRight,
|
bgColBorderSidesRight,
|
||||||
&sideRight,
|
&sideRight,
|
||||||
|
bgColFillWeight,
|
||||||
|
&fill_weight,
|
||||||
|
bgColBorderWeight,
|
||||||
|
&border_weight,
|
||||||
-1);
|
-1);
|
||||||
fprintf(fp, "# Background %d: %s\n", index, text ? text : "");
|
fprintf(fp, "# Background %d: %s\n", index, text ? text : "");
|
||||||
fprintf(fp, "rounded = %d\n", r);
|
fprintf(fp, "rounded = %d\n", r);
|
||||||
@@ -245,6 +251,9 @@ void config_write_backgrounds(FILE *fp)
|
|||||||
strcat(sides, "R");
|
strcat(sides, "R");
|
||||||
fprintf(fp, "border_sides = %s\n", 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, "background_color", *fillColor, fillOpacity);
|
||||||
config_write_color(fp, "border_color", *borderColor, borderOpacity);
|
config_write_color(fp, "border_color", *borderColor, borderOpacity);
|
||||||
if (gradient_id >= 0)
|
if (gradient_id >= 0)
|
||||||
@@ -517,6 +526,11 @@ void config_write_task(FILE *fp)
|
|||||||
if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(task_font_set)))
|
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_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_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
|
// same for: "" _normal _active _urgent _iconified
|
||||||
if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(task_default_color_set))) {
|
if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(task_default_color_set))) {
|
||||||
@@ -981,7 +995,7 @@ unsigned short checksum_txt(FILE *f)
|
|||||||
|
|
||||||
void config_save_file(const char *path)
|
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;
|
FILE *fp;
|
||||||
if ((fp = fopen(path, "w+t")) == NULL)
|
if ((fp = fopen(path, "w+t")) == NULL)
|
||||||
@@ -1225,6 +1239,12 @@ void add_entry(char *key, char *value)
|
|||||||
int id = gradient_index_safe(atoi(value));
|
int id = gradient_index_safe(atoi(value));
|
||||||
gtk_combo_box_set_active(GTK_COMBO_BOX(background_gradient_press), id);
|
gtk_combo_box_set_active(GTK_COMBO_BOX(background_gradient_press), id);
|
||||||
background_force_update();
|
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 */
|
/* Panel */
|
||||||
@@ -1732,6 +1752,10 @@ void add_entry(char *key, char *value)
|
|||||||
else if (strcmp(key, "task_tooltip") == 0 || strcmp(key, "tooltip") == 0) {
|
else if (strcmp(key, "task_tooltip") == 0 || strcmp(key, "tooltip") == 0) {
|
||||||
gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(tooltip_task_show), atoi(value));
|
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 */
|
/* Systray */
|
||||||
else if (strcmp(key, "systray") == 0) {
|
else if (strcmp(key, "systray") == 0) {
|
||||||
|
|||||||
@@ -177,7 +177,7 @@ gboolean update_snapshot(gpointer ignored)
|
|||||||
{
|
{
|
||||||
gchar *tint2_cache_dir = g_build_filename(g_get_user_cache_dir(), "tint2", NULL);
|
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))
|
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);
|
g_free(tint2_cache_dir);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+45
-13
@@ -54,8 +54,9 @@ void default_tooltip()
|
|||||||
void cleanup_tooltip()
|
void cleanup_tooltip()
|
||||||
{
|
{
|
||||||
stop_tooltip_timeout();
|
stop_tooltip_timeout();
|
||||||
|
stop_timeout(g_tooltip.update_timeout);
|
||||||
tooltip_hide(NULL);
|
tooltip_hide(NULL);
|
||||||
tooltip_copy_text(NULL);
|
tooltip_update_contents_for(NULL);
|
||||||
if (g_tooltip.window)
|
if (g_tooltip.window)
|
||||||
XDestroyWindow(server.display, g_tooltip.window);
|
XDestroyWindow(server.display, g_tooltip.window);
|
||||||
g_tooltip.window = 0;
|
g_tooltip.window = 0;
|
||||||
@@ -118,7 +119,7 @@ void tooltip_trigger_show(Area *area, Panel *p, XEvent *e)
|
|||||||
just_shown = TRUE;
|
just_shown = TRUE;
|
||||||
g_tooltip.panel = p;
|
g_tooltip.panel = p;
|
||||||
if (g_tooltip.mapped && g_tooltip.area != area) {
|
if (g_tooltip.mapped && g_tooltip.area != area) {
|
||||||
tooltip_copy_text(area);
|
tooltip_update_contents_for(area);
|
||||||
tooltip_update();
|
tooltip_update();
|
||||||
stop_tooltip_timeout();
|
stop_tooltip_timeout();
|
||||||
} else if (!g_tooltip.mapped) {
|
} else if (!g_tooltip.mapped) {
|
||||||
@@ -133,7 +134,7 @@ void tooltip_show(void *arg)
|
|||||||
XTranslateCoordinates(server.display, server.root_win, g_tooltip.panel->main_win, x, y, &mx, &my, &w);
|
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);
|
Area *area = find_area_under_mouse(g_tooltip.panel, mx, my);
|
||||||
if (!g_tooltip.mapped && area->_get_tooltip_text) {
|
if (!g_tooltip.mapped && area->_get_tooltip_text) {
|
||||||
tooltip_copy_text(area);
|
tooltip_update_contents_for(area);
|
||||||
g_tooltip.mapped = True;
|
g_tooltip.mapped = True;
|
||||||
XMapWindow(server.display, g_tooltip.window);
|
XMapWindow(server.display, g_tooltip.window);
|
||||||
tooltip_update();
|
tooltip_update();
|
||||||
@@ -144,7 +145,7 @@ void tooltip_show(void *arg)
|
|||||||
void tooltip_update_geometry()
|
void tooltip_update_geometry()
|
||||||
{
|
{
|
||||||
Panel *panel = g_tooltip.panel;
|
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_surface_t *cs = cairo_xlib_surface_create(server.display, g_tooltip.window, server.visual, width, height);
|
||||||
cairo_t *c = cairo_create(cs);
|
cairo_t *c = cairo_create(cs);
|
||||||
@@ -152,16 +153,25 @@ void tooltip_update_geometry()
|
|||||||
|
|
||||||
pango_layout_set_font_description(layout, g_tooltip.font_desc);
|
pango_layout_set_font_description(layout, g_tooltip.font_desc);
|
||||||
PangoRectangle r1, r2;
|
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);
|
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_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_set_wrap(layout, PANGO_WRAP_WORD);
|
||||||
pango_layout_get_pixel_extents(layout, &r1, &r2);
|
pango_layout_get_pixel_extents(layout, &r1, &r2);
|
||||||
width = left_right_bg_border_width(g_tooltip.bg) + 2 * g_tooltip.paddingx + r2.width;
|
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;
|
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)
|
if (panel_horizontal && panel_position & BOTTOM)
|
||||||
y = panel->posy - height;
|
y = panel->posy - height;
|
||||||
@@ -278,8 +288,16 @@ void tooltip_update()
|
|||||||
-r1.x / 2 + left_bg_border_width(g_tooltip.bg) + g_tooltip.paddingx,
|
-r1.x / 2 + left_bg_border_width(g_tooltip.bg) + g_tooltip.paddingx,
|
||||||
-r1.y / 2 + 1 + top_bg_border_width(g_tooltip.bg) + g_tooltip.paddingy);
|
-r1.y / 2 + 1 + top_bg_border_width(g_tooltip.bg) + g_tooltip.paddingy);
|
||||||
pango_cairo_show_layout(c, layout);
|
pango_cairo_show_layout(c, layout);
|
||||||
|
|
||||||
g_object_unref(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_destroy(c);
|
||||||
cairo_surface_destroy(cs);
|
cairo_surface_destroy(cs);
|
||||||
}
|
}
|
||||||
@@ -287,7 +305,7 @@ void tooltip_update()
|
|||||||
void tooltip_trigger_hide()
|
void tooltip_trigger_hide()
|
||||||
{
|
{
|
||||||
if (g_tooltip.mapped) {
|
if (g_tooltip.mapped) {
|
||||||
tooltip_copy_text(0);
|
tooltip_update_contents_for(NULL);
|
||||||
start_hide_timeout();
|
start_hide_timeout();
|
||||||
} else {
|
} else {
|
||||||
// tooltip not visible yet, but maybe a timeout is still pending
|
// tooltip not visible yet, but maybe a timeout is still pending
|
||||||
@@ -302,6 +320,7 @@ void tooltip_hide(void *arg)
|
|||||||
XUnmapWindow(server.display, g_tooltip.window);
|
XUnmapWindow(server.display, g_tooltip.window);
|
||||||
XFlush(server.display);
|
XFlush(server.display);
|
||||||
}
|
}
|
||||||
|
g_tooltip.area = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
void start_show_timeout()
|
void start_show_timeout()
|
||||||
@@ -319,12 +338,25 @@ void stop_tooltip_timeout()
|
|||||||
stop_timeout(g_tooltip.timeout);
|
stop_timeout(g_tooltip.timeout);
|
||||||
}
|
}
|
||||||
|
|
||||||
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)
|
if (area && area->_get_tooltip_text)
|
||||||
g_tooltip.tooltip_text = area->_get_tooltip_text(area);
|
g_tooltip.tooltip_text = area->_get_tooltip_text(area);
|
||||||
else
|
if (area && area->_get_tooltip_image) {
|
||||||
g_tooltip.tooltip_text = NULL;
|
g_tooltip.image = area->_get_tooltip_image(area);
|
||||||
|
if (g_tooltip.image)
|
||||||
|
cairo_surface_reference(g_tooltip.image);
|
||||||
|
else
|
||||||
|
change_timeout(&g_tooltip.update_timeout, 300, 0, tooltip_update_contents_timeout, NULL);
|
||||||
|
}
|
||||||
g_tooltip.area = area;
|
g_tooltip.area = area;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -37,6 +37,8 @@ typedef struct {
|
|||||||
Color font_color;
|
Color font_color;
|
||||||
Background *bg;
|
Background *bg;
|
||||||
timeout *timeout;
|
timeout *timeout;
|
||||||
|
timeout *update_timeout;
|
||||||
|
cairo_surface_t *image;
|
||||||
} Tooltip;
|
} Tooltip;
|
||||||
|
|
||||||
extern Tooltip g_tooltip;
|
extern Tooltip g_tooltip;
|
||||||
@@ -53,7 +55,7 @@ void tooltip_show(void * /*arg*/);
|
|||||||
void tooltip_update();
|
void tooltip_update();
|
||||||
void tooltip_trigger_hide();
|
void tooltip_trigger_hide();
|
||||||
void tooltip_hide(void * /*arg*/);
|
void tooltip_hide(void * /*arg*/);
|
||||||
void tooltip_copy_text(Area *area);
|
void tooltip_update_contents_for(Area *area);
|
||||||
void tooltip_default_font_changed();
|
void tooltip_default_font_changed();
|
||||||
|
|
||||||
#endif // TOOLTIP_H
|
#endif // TOOLTIP_H
|
||||||
|
|||||||
+164
@@ -0,0 +1,164 @@
|
|||||||
|
#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
|
||||||
|
char *result = (char*) calloc(32, 1);
|
||||||
|
sprintf(result, "%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
|
||||||
@@ -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
|
||||||
@@ -0,0 +1,238 @@
|
|||||||
|
/* addr2line.c -- convert addresses to line number and function name
|
||||||
|
Copyright 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006,
|
||||||
|
2007, 2009 Free Software Foundation, Inc.
|
||||||
|
Contributed by Ulrich Lauther <Ulrich.Lauther@mchp.siemens.de>
|
||||||
|
|
||||||
|
This file is part of GNU Binutils.
|
||||||
|
|
||||||
|
This program is free software; you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation; either version 3, or (at your option)
|
||||||
|
any later version.
|
||||||
|
|
||||||
|
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, 51 Franklin Street - Fifth Floor, Boston,
|
||||||
|
MA 02110-1301, USA. */
|
||||||
|
|
||||||
|
/* Derived from objdump.c and nm.c by Ulrich.Lauther@mchp.siemens.de */
|
||||||
|
|
||||||
|
#ifdef ENABLE_EXECINFO
|
||||||
|
|
||||||
|
#include <bfd.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#include "addr2line.h"
|
||||||
|
#include "print.h"
|
||||||
|
|
||||||
|
static bfd_boolean unwind_inlines = 1; /* -i, unwind inlined functions. */
|
||||||
|
static bfd_boolean with_addresses = 0; /* -a, show addresses. */
|
||||||
|
static bfd_boolean with_functions = 1; /* -f, show function names. */
|
||||||
|
static bfd_boolean do_demangle = 1; /* -C, demangle names. */
|
||||||
|
static bfd_boolean base_names = 1; /* -s, strip directory names. */
|
||||||
|
|
||||||
|
typedef struct Lookup {
|
||||||
|
char *exe_file_name;
|
||||||
|
void *address;
|
||||||
|
char *result;
|
||||||
|
bfd *abfd;
|
||||||
|
asymbol **syms;
|
||||||
|
bfd_vma pc;
|
||||||
|
const char *filename;
|
||||||
|
const char *functionname;
|
||||||
|
unsigned int line;
|
||||||
|
unsigned int discriminator;
|
||||||
|
bfd_boolean found;
|
||||||
|
} Lookup;
|
||||||
|
|
||||||
|
static asymbol **slurp_symtab(bfd *);
|
||||||
|
static void find_address_in_section(bfd *, asection *, void *unused);
|
||||||
|
static void translate_address(Lookup *lookup);
|
||||||
|
|
||||||
|
static asymbol **slurp_symtab(bfd *abfd)
|
||||||
|
{
|
||||||
|
if ((bfd_get_file_flags(abfd) & HAS_SYMS) == 0)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
long storage = bfd_get_symtab_upper_bound(abfd);
|
||||||
|
bfd_boolean dynamic = FALSE;
|
||||||
|
if (storage == 0) {
|
||||||
|
storage = bfd_get_dynamic_symtab_upper_bound(abfd);
|
||||||
|
dynamic = TRUE;
|
||||||
|
}
|
||||||
|
if (storage < 0)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
asymbol **syms = (asymbol **)malloc(storage);
|
||||||
|
long symcount;
|
||||||
|
if (dynamic)
|
||||||
|
symcount = bfd_canonicalize_dynamic_symtab(abfd, syms);
|
||||||
|
else
|
||||||
|
symcount = bfd_canonicalize_symtab(abfd, syms);
|
||||||
|
if (symcount < 0)
|
||||||
|
return syms;
|
||||||
|
|
||||||
|
// If there are no symbols left after canonicalization and
|
||||||
|
// we have not tried the dynamic symbols then give them a go.
|
||||||
|
if (symcount == 0 && !dynamic && (storage = bfd_get_dynamic_symtab_upper_bound(abfd)) > 0) {
|
||||||
|
free(syms);
|
||||||
|
syms = (asymbol **)malloc(storage);
|
||||||
|
symcount = bfd_canonicalize_dynamic_symtab(abfd, syms);
|
||||||
|
}
|
||||||
|
return syms;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void find_address_in_section(bfd *abfd, asection *section, void *unused)
|
||||||
|
{
|
||||||
|
Lookup *lookup = (Lookup *)abfd->usrdata;
|
||||||
|
|
||||||
|
bfd_vma vma;
|
||||||
|
bfd_size_type size;
|
||||||
|
|
||||||
|
if (lookup->found)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if ((bfd_get_section_flags(abfd, section) & SEC_ALLOC) == 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
vma = bfd_get_section_vma(abfd, section);
|
||||||
|
if (lookup->pc < vma)
|
||||||
|
return;
|
||||||
|
|
||||||
|
size = bfd_get_section_size(section);
|
||||||
|
if (lookup->pc >= vma + size)
|
||||||
|
return;
|
||||||
|
|
||||||
|
lookup->found = bfd_find_nearest_line_discriminator(abfd,
|
||||||
|
section,
|
||||||
|
lookup->syms,
|
||||||
|
lookup->pc - vma,
|
||||||
|
&lookup->filename,
|
||||||
|
&lookup->functionname,
|
||||||
|
&lookup->line,
|
||||||
|
&lookup->discriminator);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void translate_address(Lookup *lookup)
|
||||||
|
{
|
||||||
|
Buffer *buffer = NULL;
|
||||||
|
lookup->pc = (bfd_vma)lookup->address;
|
||||||
|
|
||||||
|
if (with_addresses) {
|
||||||
|
char tmp[256];
|
||||||
|
bfd_sprintf_vma(lookup->abfd, tmp, lookup->pc);
|
||||||
|
buffer = buffer_printf(buffer, "0x%s: ", tmp);
|
||||||
|
}
|
||||||
|
|
||||||
|
lookup->found = FALSE;
|
||||||
|
bfd_map_over_sections(lookup->abfd, find_address_in_section, NULL);
|
||||||
|
|
||||||
|
if (!lookup->found) {
|
||||||
|
if (with_functions)
|
||||||
|
buffer = buffer_printf(buffer, "?? ");
|
||||||
|
buffer = buffer_printf(buffer, "??:0");
|
||||||
|
} else {
|
||||||
|
while (1) {
|
||||||
|
if (with_functions) {
|
||||||
|
const char *name;
|
||||||
|
char *alloc = NULL;
|
||||||
|
|
||||||
|
name = lookup->functionname;
|
||||||
|
if (name == NULL || *name == '\0')
|
||||||
|
name = "??";
|
||||||
|
else if (do_demangle) {
|
||||||
|
alloc = bfd_demangle(lookup->abfd, name, 3);
|
||||||
|
if (alloc != NULL)
|
||||||
|
name = alloc;
|
||||||
|
}
|
||||||
|
|
||||||
|
buffer = buffer_printf(buffer, "%s", name);
|
||||||
|
buffer = buffer_printf(buffer, " at ");
|
||||||
|
|
||||||
|
if (alloc != NULL)
|
||||||
|
free(alloc);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (base_names && lookup->filename != NULL) {
|
||||||
|
const char *h = strrchr(lookup->filename, '/');
|
||||||
|
if (h != NULL)
|
||||||
|
lookup->filename = h + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
buffer = buffer_printf(buffer, "%s:", lookup->filename ? lookup->filename : "??");
|
||||||
|
if (lookup->line != 0) {
|
||||||
|
if (lookup->discriminator != 0)
|
||||||
|
buffer = buffer_printf(buffer, "%u (discriminator %u)", lookup->line, lookup->discriminator);
|
||||||
|
else
|
||||||
|
buffer = buffer_printf(buffer, "%u", lookup->line);
|
||||||
|
} else {
|
||||||
|
buffer = buffer_printf(buffer, "??");
|
||||||
|
}
|
||||||
|
if (!unwind_inlines)
|
||||||
|
lookup->found = FALSE;
|
||||||
|
else
|
||||||
|
lookup->found =
|
||||||
|
bfd_find_inliner_info(lookup->abfd, &lookup->filename, &lookup->functionname, &lookup->line);
|
||||||
|
if (!lookup->found)
|
||||||
|
break;
|
||||||
|
buffer = buffer_printf(buffer, " (inlined by) ");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (buffer)
|
||||||
|
lookup->result = buffer->data;
|
||||||
|
}
|
||||||
|
|
||||||
|
Lookup *addr2line_init(const char *file_name)
|
||||||
|
{
|
||||||
|
bfd *abfd = bfd_openr(file_name, NULL);
|
||||||
|
if (abfd == NULL)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
// Decompress sections.
|
||||||
|
abfd->flags |= BFD_DECOMPRESS;
|
||||||
|
|
||||||
|
if (bfd_check_format(abfd, bfd_archive))
|
||||||
|
goto err;
|
||||||
|
|
||||||
|
char **matching = NULL;
|
||||||
|
if (!bfd_check_format_matches(abfd, bfd_object, &matching)) {
|
||||||
|
free(matching);
|
||||||
|
goto err;
|
||||||
|
}
|
||||||
|
|
||||||
|
Lookup *lookup = (Lookup *)calloc(1, sizeof(Lookup));
|
||||||
|
lookup->abfd = abfd;
|
||||||
|
lookup->abfd->usrdata = lookup;
|
||||||
|
lookup->syms = slurp_symtab(abfd);
|
||||||
|
lookup->exe_file_name = strdup(file_name);
|
||||||
|
return lookup;
|
||||||
|
|
||||||
|
err:
|
||||||
|
bfd_close(abfd);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
void addr2line_destroy(Lookup *lookup)
|
||||||
|
{
|
||||||
|
free(lookup->syms);
|
||||||
|
bfd_close(lookup->abfd);
|
||||||
|
free(lookup->exe_file_name);
|
||||||
|
free(lookup);
|
||||||
|
}
|
||||||
|
|
||||||
|
char *addr2line_lookup(Lookup *lookup, void *address)
|
||||||
|
{
|
||||||
|
lookup->address = address;
|
||||||
|
lookup->result = NULL;
|
||||||
|
translate_address(lookup);
|
||||||
|
return lookup->result;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
#ifndef ADDR2LINE
|
||||||
|
#define ADDR2LINE
|
||||||
|
|
||||||
|
#ifdef ENABLE_EXECINFO
|
||||||
|
|
||||||
|
typedef struct Lookup Lookup;
|
||||||
|
|
||||||
|
Lookup *addr2line_init(const char *file_name);
|
||||||
|
void addr2line_destroy(Lookup *lookup);
|
||||||
|
char *addr2line_lookup(Lookup *lookup, void *address);
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif
|
||||||
+106
-65
@@ -224,7 +224,7 @@ int compute_desired_size(Area *a)
|
|||||||
if (a->_compute_desired_size)
|
if (a->_compute_desired_size)
|
||||||
return a->_compute_desired_size(a);
|
return a->_compute_desired_size(a);
|
||||||
if (a->size_mode == LAYOUT_FIXED)
|
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);
|
return container_compute_desired_size(a);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -385,7 +385,7 @@ void draw_tree(Area *a)
|
|||||||
a->posx,
|
a->posx,
|
||||||
a->posy);
|
a->posy);
|
||||||
else
|
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)
|
for (GList *l = a->children; l; l = l->next)
|
||||||
draw_tree((Area *)l->data);
|
draw_tree((Area *)l->data);
|
||||||
@@ -490,28 +490,59 @@ void draw(Area *a)
|
|||||||
cairo_surface_destroy(cs);
|
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)
|
void draw_background(Area *a, cairo_t *c)
|
||||||
{
|
{
|
||||||
if ((a->bg->fill_color.alpha > 0.0) ||
|
if ((a->bg->fill_color.alpha > 0.0) ||
|
||||||
(panel_config.mouse_effects && (a->has_mouse_over_effect || a->has_mouse_press_effect))) {
|
(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
|
// Not sure about this
|
||||||
draw_rect(c,
|
draw_rect(c,
|
||||||
left_border_width(a),
|
left_border_width(a),
|
||||||
@@ -519,7 +550,7 @@ void draw_background(Area *a, cairo_t *c)
|
|||||||
a->width - left_right_border_width(a),
|
a->width - left_right_border_width(a),
|
||||||
a->height - top_bottom_border_width(a),
|
a->height - top_bottom_border_width(a),
|
||||||
a->bg->border.radius - a->bg->border.width / 1.571);
|
a->bg->border.radius - a->bg->border.width / 1.571);
|
||||||
|
set_cairo_source_bg_color(a, c);
|
||||||
cairo_fill(c);
|
cairo_fill(c);
|
||||||
}
|
}
|
||||||
for (GList *l = a->gradient_instances_by_state[a->mouse_state]; l; l = l->next) {
|
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);
|
cairo_set_line_width(c, a->bg->border.width);
|
||||||
|
|
||||||
// draw border inside (x, y, width, height)
|
// draw border inside (x, y, width, height)
|
||||||
if (a->mouse_state == MOUSE_OVER)
|
set_cairo_source_border_color(a, c);
|
||||||
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);
|
|
||||||
draw_rect_on_sides(c,
|
draw_rect_on_sides(c,
|
||||||
left_border_width(a) / 2.,
|
left_border_width(a) / 2.,
|
||||||
top_border_width(a) / 2.,
|
top_border_width(a) / 2.,
|
||||||
@@ -630,7 +644,7 @@ void free_area(Area *a)
|
|||||||
free_area_gradient_instances(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)
|
if (mouse_over_area == area && !area)
|
||||||
return;
|
return;
|
||||||
@@ -843,14 +857,14 @@ int top_bottom_bg_border_width(Background *bg)
|
|||||||
|
|
||||||
void area_dump_geometry(Area *area, int indent)
|
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;
|
indent += 2;
|
||||||
if (!area->on_screen) {
|
if (!area->on_screen) {
|
||||||
fprintf(stderr, "%*shidden\n", indent, "");
|
fprintf(stderr, "tint2: %*shidden\n", indent, "");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
fprintf(stderr,
|
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,
|
indent,
|
||||||
"",
|
"",
|
||||||
area->posx,
|
area->posx,
|
||||||
@@ -859,7 +873,7 @@ void area_dump_geometry(Area *area, int indent)
|
|||||||
area->height,
|
area->height,
|
||||||
compute_desired_size(area));
|
compute_desired_size(area));
|
||||||
fprintf(stderr,
|
fprintf(stderr,
|
||||||
"%*sBorder: left = %d, right = %d, top = %d, bottom = %d\n",
|
"tint2: %*sBorder: left = %d, right = %d, top = %d, bottom = %d\n",
|
||||||
indent,
|
indent,
|
||||||
"",
|
"",
|
||||||
left_border_width(area),
|
left_border_width(area),
|
||||||
@@ -867,7 +881,7 @@ void area_dump_geometry(Area *area, int indent)
|
|||||||
top_border_width(area),
|
top_border_width(area),
|
||||||
bottom_border_width(area));
|
bottom_border_width(area));
|
||||||
fprintf(stderr,
|
fprintf(stderr,
|
||||||
"%*sPadding: left = right = %d, top = bottom = %d, spacing = %d\n",
|
"tint2: %*sPadding: left = right = %d, top = bottom = %d, spacing = %d\n",
|
||||||
indent,
|
indent,
|
||||||
"",
|
"",
|
||||||
area->paddingxlr,
|
area->paddingxlr,
|
||||||
@@ -876,13 +890,40 @@ void area_dump_geometry(Area *area, int indent)
|
|||||||
if (area->_dump_geometry)
|
if (area->_dump_geometry)
|
||||||
area->_dump_geometry(area, indent);
|
area->_dump_geometry(area, indent);
|
||||||
if (area->children) {
|
if (area->children) {
|
||||||
fprintf(stderr, "%*sChildren:\n", indent, "");
|
fprintf(stderr, "tint2: %*sChildren:\n", indent, "");
|
||||||
indent += 2;
|
indent += 2;
|
||||||
for (GList *l = area->children; l; l = l->next)
|
for (GList *l = area->children; l; l = l->next)
|
||||||
area_dump_geometry((Area *)l->data, indent);
|
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,
|
void area_compute_text_geometry(Area *area,
|
||||||
const char *line1,
|
const char *line1,
|
||||||
const char *line2,
|
const char *line2,
|
||||||
@@ -895,15 +936,8 @@ void area_compute_text_geometry(Area *area,
|
|||||||
int *line2_height,
|
int *line2_height,
|
||||||
int *line2_width)
|
int *line2_width)
|
||||||
{
|
{
|
||||||
Panel *panel = (Panel *)area->panel;
|
|
||||||
int available_w, available_h;
|
int available_w, available_h;
|
||||||
if (panel_horizontal) {
|
area_compute_available_size(area, &available_w, &available_h);
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (line1 && line1[0])
|
if (line1 && line1[0])
|
||||||
get_text_size2(line1_font_desc,
|
get_text_size2(line1_font_desc,
|
||||||
@@ -959,7 +993,7 @@ int text_area_compute_desired_size(Area *area,
|
|||||||
int new_size = MAX(line1_width, line2_width) + 2 * area->paddingxlr + left_right_border_width(area);
|
int new_size = MAX(line1_width, line2_width) + 2 * area->paddingxlr + left_right_border_width(area);
|
||||||
return new_size;
|
return new_size;
|
||||||
} else {
|
} else {
|
||||||
int new_size = line1_height + line2_height + 2 * area->paddingxlr + top_bottom_border_width(area);
|
int new_size = line1_height + line2_height + 2 * area->paddingy + top_bottom_border_width(area);
|
||||||
return new_size;
|
return new_size;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -996,9 +1030,13 @@ gboolean resize_text_area(Area *area,
|
|||||||
line1_font_desc,
|
line1_font_desc,
|
||||||
line2_font_desc);
|
line2_font_desc);
|
||||||
if (panel_horizontal) {
|
if (panel_horizontal) {
|
||||||
if (new_size > area->width || new_size < (area->width - 6)) {
|
if (new_size != area->width) {
|
||||||
// we try to limit the number of resizes
|
if (new_size < area->width && abs(new_size - area->width) < 6) {
|
||||||
area->width = new_size + 1;
|
// we try to limit the number of resizes
|
||||||
|
new_size = area->width;
|
||||||
|
} else {
|
||||||
|
area->width = new_size;
|
||||||
|
}
|
||||||
*line1_posy = (area->height - line1_height) / 2;
|
*line1_posy = (area->height - line1_height) / 2;
|
||||||
if (line2) {
|
if (line2) {
|
||||||
*line1_posy -= (line2_height) / 2;
|
*line1_posy -= (line2_height) / 2;
|
||||||
@@ -1008,7 +1046,6 @@ gboolean resize_text_area(Area *area,
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (new_size != area->height) {
|
if (new_size != area->height) {
|
||||||
// we try to limit the number of resizes
|
|
||||||
area->height = new_size;
|
area->height = new_size;
|
||||||
*line1_posy = (area->height - line1_height) / 2;
|
*line1_posy = (area->height - line1_height) / 2;
|
||||||
if (line2) {
|
if (line2) {
|
||||||
@@ -1032,18 +1069,22 @@ void draw_text_area(Area *area,
|
|||||||
int line2_posy,
|
int line2_posy,
|
||||||
Color *color)
|
Color *color)
|
||||||
{
|
{
|
||||||
|
int inner_w, inner_h;
|
||||||
|
area_compute_inner_size(area, &inner_w, &inner_h);
|
||||||
|
|
||||||
PangoLayout *layout = pango_cairo_create_layout(c);
|
PangoLayout *layout = pango_cairo_create_layout(c);
|
||||||
pango_layout_set_alignment(layout, PANGO_ALIGN_CENTER);
|
pango_layout_set_alignment(layout, PANGO_ALIGN_CENTER);
|
||||||
pango_layout_set_wrap(layout, PANGO_WRAP_WORD_CHAR);
|
pango_layout_set_wrap(layout, PANGO_WRAP_WORD_CHAR);
|
||||||
pango_layout_set_ellipsize(layout, PANGO_ELLIPSIZE_NONE);
|
pango_layout_set_ellipsize(layout, PANGO_ELLIPSIZE_NONE);
|
||||||
pango_layout_set_width(layout, area->width * PANGO_SCALE);
|
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);
|
cairo_set_source_rgba(c, color->rgb[0], color->rgb[1], color->rgb[2], color->alpha);
|
||||||
|
|
||||||
if (line1 && line1[0]) {
|
if (line1 && line1[0]) {
|
||||||
pango_layout_set_font_description(layout, line1_font_desc);
|
pango_layout_set_font_description(layout, line1_font_desc);
|
||||||
pango_layout_set_text(layout, line1, strlen(line1));
|
pango_layout_set_text(layout, line1, strlen(line1));
|
||||||
pango_cairo_update_layout(c, layout);
|
pango_cairo_update_layout(c, layout);
|
||||||
draw_text(layout, c, 0, line1_posy, color, ((Panel *)area->panel)->font_shadow);
|
draw_text(layout, c, (area->width - inner_w) / 2, line1_posy, color, ((Panel *)area->panel)->font_shadow);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (line2 && line2[0]) {
|
if (line2 && line2[0]) {
|
||||||
@@ -1051,7 +1092,7 @@ void draw_text_area(Area *area,
|
|||||||
pango_layout_set_indent(layout, 0);
|
pango_layout_set_indent(layout, 0);
|
||||||
pango_layout_set_text(layout, line2, strlen(line2));
|
pango_layout_set_text(layout, line2, strlen(line2));
|
||||||
pango_cairo_update_layout(c, layout);
|
pango_cairo_update_layout(c, layout);
|
||||||
draw_text(layout, c, 0, line2_posy, color, ((Panel *)area->panel)->font_shadow);
|
draw_text(layout, c, (area->width - inner_w) / 2, line2_posy, color, ((Panel *)area->panel)->font_shadow);
|
||||||
}
|
}
|
||||||
|
|
||||||
g_object_unref(layout);
|
g_object_unref(layout);
|
||||||
@@ -1129,7 +1170,7 @@ void free_gradient_instance(GradientInstance *gi)
|
|||||||
void instantiate_area_gradients(Area *area)
|
void instantiate_area_gradients(Area *area)
|
||||||
{
|
{
|
||||||
if (debug_gradients)
|
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++) {
|
for (int i = 0; i < MOUSE_STATE_COUNT; i++) {
|
||||||
g_assert_null(area->gradient_instances_by_state[i]);
|
g_assert_null(area->gradient_instances_by_state[i]);
|
||||||
GradientClass *g = area->bg->gradients[i];
|
GradientClass *g = area->bg->gradients[i];
|
||||||
@@ -1144,7 +1185,7 @@ void instantiate_area_gradients(Area *area)
|
|||||||
void free_area_gradient_instances(Area *area)
|
void free_area_gradient_instances(Area *area)
|
||||||
{
|
{
|
||||||
if (debug_gradients)
|
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 (int i = 0; i < MOUSE_STATE_COUNT; i++) {
|
||||||
for (GList *l = area->gradient_instances_by_state[i]; l; l = l->next) {
|
for (GList *l = area->gradient_instances_by_state[i]; l; l = l->next) {
|
||||||
GradientInstance *gi = (GradientInstance *)l->data;
|
GradientInstance *gi = (GradientInstance *)l->data;
|
||||||
@@ -1170,7 +1211,7 @@ double compute_control_point_offset(Area *area, Offset *offset)
|
|||||||
double height = element_area->height;
|
double height = element_area->height;
|
||||||
double radius = sqrt(element_area->width * element_area->width + element_area->height * element_area->height) / 2.0;
|
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) {
|
if (offset->element == ELEMENT_SELF) {
|
||||||
left = 0;
|
left = 0;
|
||||||
top = 0;
|
top = 0;
|
||||||
|
|||||||
+6
-1
@@ -154,6 +154,8 @@ typedef struct Background {
|
|||||||
Color border_color_pressed;
|
Color border_color_pressed;
|
||||||
// Pointer to a GradientClass or NULL, no ownership
|
// Pointer to a GradientClass or NULL, no ownership
|
||||||
GradientClass *gradients[MOUSE_STATE_COUNT];
|
GradientClass *gradients[MOUSE_STATE_COUNT];
|
||||||
|
double fill_content_tint_weight;
|
||||||
|
double border_content_tint_weight;
|
||||||
} Background;
|
} Background;
|
||||||
|
|
||||||
typedef enum Layout {
|
typedef enum Layout {
|
||||||
@@ -233,6 +235,7 @@ typedef struct Area {
|
|||||||
// Returns a copy of the tooltip to be displayed for this widget.
|
// Returns a copy of the tooltip to be displayed for this widget.
|
||||||
// The caller takes ownership of the pointer.
|
// The caller takes ownership of the pointer.
|
||||||
char *(*_get_tooltip_text)(void *obj);
|
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.
|
// 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.
|
// 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.
|
// Prints the geometry of the object on stderr, with left indentation of indent spaces.
|
||||||
void (*_dump_geometry)(void *obj, int indent);
|
void (*_dump_geometry)(void *obj, int indent);
|
||||||
|
|
||||||
|
void (*_get_content_color)(void *obj, Color *color);
|
||||||
} Area;
|
} Area;
|
||||||
|
|
||||||
// Initializes the Background member to default values.
|
// Initializes the Background member to default values.
|
||||||
@@ -357,7 +362,7 @@ void free_area_gradient_instances(Area *area);
|
|||||||
|
|
||||||
void area_dump_geometry(Area *area, int indent);
|
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 mouse_out();
|
||||||
|
|
||||||
void update_gradient(GradientInstance *gi);
|
void update_gradient(GradientInstance *gi);
|
||||||
|
|||||||
+2
-2
@@ -117,14 +117,14 @@ void save_cache(Cache *cache, const gchar *cache_path)
|
|||||||
fd = open(cache_path, O_RDONLY | O_CREAT, 0600);
|
fd = open(cache_path, O_RDONLY | O_CREAT, 0600);
|
||||||
}
|
}
|
||||||
if (fd == -1) {
|
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;
|
return;
|
||||||
}
|
}
|
||||||
flock(fd, LOCK_EX);
|
flock(fd, LOCK_EX);
|
||||||
|
|
||||||
FILE *f = fopen(cache_path, "w");
|
FILE *f = fopen(cache_path, "w");
|
||||||
if (!f) {
|
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;
|
goto unlock;
|
||||||
}
|
}
|
||||||
g_hash_table_foreach(cache->_table, write_cache_line, f);
|
g_hash_table_foreach(cache->_table, write_cache_line, f);
|
||||||
|
|||||||
+279
-16
@@ -34,17 +34,177 @@
|
|||||||
#include "../server.h"
|
#include "../server.h"
|
||||||
#include <sys/wait.h>
|
#include <sys/wait.h>
|
||||||
#include <sys/types.h>
|
#include <sys/types.h>
|
||||||
|
#include <pwd.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
#include <fcntl.h>
|
#include <fcntl.h>
|
||||||
#include <sys/time.h>
|
#include <sys/time.h>
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
#include <dirent.h>
|
#include <dirent.h>
|
||||||
|
#if !defined(__OpenBSD__)
|
||||||
|
#include <wordexp.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
#ifdef HAVE_RSVG
|
#ifdef HAVE_RSVG
|
||||||
#include <librsvg/rsvg.h>
|
#include <librsvg/rsvg.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifdef ENABLE_LIBUNWIND
|
||||||
|
#define UNW_LOCAL_ONLY
|
||||||
|
#include <libunwind.h>
|
||||||
|
#else
|
||||||
|
#ifdef ENABLE_EXECINFO
|
||||||
|
#include <execinfo.h>
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
#include "../panel.h"
|
#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];
|
||||||
|
sprintf(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)
|
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) {
|
while ((nb = fread(buffer, 1, sizeof(buffer), file_src)) > 0) {
|
||||||
if (nb != fwrite(buffer, 1, nb, file_dest)) {
|
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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -111,7 +271,15 @@ int setenvd(const char *name, const int value)
|
|||||||
}
|
}
|
||||||
|
|
||||||
#ifndef TINT2CONF
|
#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)
|
if (!command || strlen(command) == 0)
|
||||||
return -1;
|
return -1;
|
||||||
@@ -206,7 +374,7 @@ pid_t tint_exec(const char *command, const char *dir, const char *tooltip, Time
|
|||||||
|
|
||||||
#if HAVE_SN
|
#if HAVE_SN
|
||||||
SnLauncherContext *ctx = 0;
|
SnLauncherContext *ctx = 0;
|
||||||
if (startup_notifications && time) {
|
if (startup_notifications && startup_notification && time) {
|
||||||
ctx = sn_launcher_context_new(server.sn_display, server.screen);
|
ctx = sn_launcher_context_new(server.sn_display, server.screen);
|
||||||
sn_launcher_context_set_name(ctx, tooltip);
|
sn_launcher_context_set_name(ctx, tooltip);
|
||||||
sn_launcher_context_set_description(ctx, "Application launched from tint2");
|
sn_launcher_context_set_description(ctx, "Application launched from tint2");
|
||||||
@@ -217,11 +385,11 @@ pid_t tint_exec(const char *command, const char *dir, const char *tooltip, Time
|
|||||||
pid_t pid;
|
pid_t pid;
|
||||||
pid = fork();
|
pid = fork();
|
||||||
if (pid < 0) {
|
if (pid < 0) {
|
||||||
fprintf(stderr, "Could not fork\n");
|
fprintf(stderr, "tint2: Could not fork\n");
|
||||||
} else if (pid == 0) {
|
} else if (pid == 0) {
|
||||||
// Child process
|
// Child process
|
||||||
#if HAVE_SN
|
#if HAVE_SN
|
||||||
if (startup_notifications && time) {
|
if (startup_notifications && startup_notification && time) {
|
||||||
sn_launcher_context_setup_child_process(ctx);
|
sn_launcher_context_setup_child_process(ctx);
|
||||||
}
|
}
|
||||||
#endif // HAVE_SN
|
#endif // HAVE_SN
|
||||||
@@ -230,10 +398,27 @@ pid_t tint_exec(const char *command, const char *dir, const char *tooltip, Time
|
|||||||
// Run the command
|
// Run the command
|
||||||
if (dir)
|
if (dir)
|
||||||
chdir(dir);
|
chdir(dir);
|
||||||
execl("/bin/sh", "/bin/sh", "-c", command, NULL);
|
close_all_fds();
|
||||||
fprintf(stderr, "Failed to execlp %s\n", command);
|
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 HAVE_SN
|
||||||
if (startup_notifications && time) {
|
if (startup_notifications && startup_notification && time) {
|
||||||
sn_launcher_context_unref(ctx);
|
sn_launcher_context_unref(ctx);
|
||||||
}
|
}
|
||||||
#endif // HAVE_SN
|
#endif // HAVE_SN
|
||||||
@@ -241,7 +426,7 @@ pid_t tint_exec(const char *command, const char *dir, const char *tooltip, Time
|
|||||||
} else {
|
} else {
|
||||||
// Parent process
|
// Parent process
|
||||||
#if HAVE_SN
|
#if HAVE_SN
|
||||||
if (startup_notifications && time) {
|
if (startup_notifications && startup_notification && time) {
|
||||||
g_tree_insert(server.pids, GINT_TO_POINTER(pid), ctx);
|
g_tree_insert(server.pids, GINT_TO_POINTER(pid), ctx);
|
||||||
}
|
}
|
||||||
#endif // HAVE_SN
|
#endif // HAVE_SN
|
||||||
@@ -268,7 +453,7 @@ pid_t tint_exec(const char *command, const char *dir, const char *tooltip, Time
|
|||||||
|
|
||||||
void tint_exec_no_sn(const char *command)
|
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
|
#endif
|
||||||
|
|
||||||
@@ -619,13 +804,13 @@ Imlib_Image load_image(const char *path, int cached)
|
|||||||
RsvgHandle *svg = rsvg_handle_new_from_file(path, &err);
|
RsvgHandle *svg = rsvg_handle_new_from_file(path, &err);
|
||||||
|
|
||||||
if (err != NULL) {
|
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);
|
g_error_free(err);
|
||||||
} else {
|
} else {
|
||||||
GdkPixbuf *pixbuf = rsvg_handle_get_pixbuf(svg);
|
GdkPixbuf *pixbuf = rsvg_handle_get_pixbuf(svg);
|
||||||
gdk_pixbuf_save(pixbuf, tmp_filename, "png", NULL, NULL);
|
gdk_pixbuf_save(pixbuf, tmp_filename, "png", NULL, NULL);
|
||||||
}
|
}
|
||||||
exit(0);
|
_exit(0);
|
||||||
} else {
|
} else {
|
||||||
// Parent
|
// Parent
|
||||||
close(fd);
|
close(fd);
|
||||||
@@ -752,9 +937,9 @@ void get_text_size2(const PangoFontDescription *font,
|
|||||||
|
|
||||||
available_width = MAX(0, available_width);
|
available_width = MAX(0, available_width);
|
||||||
available_height = MAX(0, available_height);
|
available_height = MAX(0, available_height);
|
||||||
Pixmap pmap = XCreatePixmap(server.display, server.root_win, available_height, available_width, server.depth);
|
|
||||||
|
|
||||||
cairo_surface_t *cs = cairo_xlib_surface_create(server.display, pmap, server.visual, available_height, available_width);
|
cairo_surface_t *cs =
|
||||||
|
cairo_image_surface_create(CAIRO_FORMAT_ARGB32, available_height, available_width);
|
||||||
cairo_t *c = cairo_create(cs);
|
cairo_t *c = cairo_create(cs);
|
||||||
|
|
||||||
PangoLayout *layout = pango_cairo_create_layout(c);
|
PangoLayout *layout = pango_cairo_create_layout(c);
|
||||||
@@ -773,12 +958,11 @@ void get_text_size2(const PangoFontDescription *font,
|
|||||||
*height_ink = rect_ink.height;
|
*height_ink = rect_ink.height;
|
||||||
*height = rect.height;
|
*height = rect.height;
|
||||||
*width = rect.width;
|
*width = rect.width;
|
||||||
// printf("dimension : %d - %d\n", rect_ink.height, rect.height);
|
// fprintf(stderr, "tint2: dimension : %d - %d\n", rect_ink.height, rect.height);
|
||||||
|
|
||||||
g_object_unref(layout);
|
g_object_unref(layout);
|
||||||
cairo_destroy(c);
|
cairo_destroy(c);
|
||||||
cairo_surface_destroy(cs);
|
cairo_surface_destroy(cs);
|
||||||
XFreePixmap(server.display, pmap);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#if !GLIB_CHECK_VERSION(2, 34, 0)
|
#if !GLIB_CHECK_VERSION(2, 34, 0)
|
||||||
@@ -848,3 +1032,82 @@ gint cmp_ptr(gconstpointer a, gconstpointer b)
|
|||||||
else
|
else
|
||||||
return 1;
|
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.;
|
||||||
|
}
|
||||||
|
|||||||
+28
-1
@@ -41,6 +41,18 @@ typedef enum MouseAction {
|
|||||||
|
|
||||||
#define ALL_DESKTOPS 0xFFFFFFFF
|
#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
|
// Copies a file to another path
|
||||||
void copy_file(const char *path_src, const char *path_dest);
|
void copy_file(const char *path_src, const char *path_dest);
|
||||||
|
|
||||||
@@ -54,7 +66,15 @@ 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);
|
void extract_values_4(const char *value, char **value1, char **value2, char **value3, char **value4);
|
||||||
|
|
||||||
// Executes a command in a shell.
|
// 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);
|
void tint_exec_no_sn(const char *command);
|
||||||
int setenvd(const char *name, const int value);
|
int setenvd(const char *name, const int value);
|
||||||
|
|
||||||
@@ -89,6 +109,7 @@ Imlib_Image load_image(const char *path, int cached);
|
|||||||
// * 1 = white
|
// * 1 = white
|
||||||
void adjust_asb(DATA32 *data, int w, int h, float alpha_adjust, float satur_adjust, float bright_adjust);
|
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);
|
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);
|
void create_heuristic_mask(DATA32 *data, int w, int h);
|
||||||
|
|
||||||
@@ -116,6 +137,8 @@ void draw_rect_on_sides(cairo_t *c, double x, double y, double w, double h, doub
|
|||||||
// Clears the pixmap (with transparent color)
|
// Clears the pixmap (with transparent color)
|
||||||
void clear_pixmap(Pixmap p, int x, int y, int w, int h);
|
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 ":").
|
// 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.
|
// Optional suffixes are added to each directory. The suffix arguments MUST end with NULL.
|
||||||
// Returns the new value of the list.
|
// Returns the new value of the list.
|
||||||
@@ -126,6 +149,10 @@ GSList *slist_remove_duplicates(GSList *list, GCompareFunc eq, GDestroyNotify fr
|
|||||||
// A trivial pointer comparator.
|
// A trivial pointer comparator.
|
||||||
gint cmp_ptr(gconstpointer a, gconstpointer b);
|
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);
|
||||||
|
|
||||||
#define free_and_null(p) \
|
#define free_and_null(p) \
|
||||||
{ \
|
{ \
|
||||||
free(p); \
|
free(p); \
|
||||||
|
|||||||
@@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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
|
||||||
+1
-1
@@ -40,7 +40,7 @@ GradientType gradient_type_from_string(const char *str)
|
|||||||
return GRADIENT_VERTICAL;
|
return GRADIENT_VERTICAL;
|
||||||
if (g_str_equal(str, "radial"))
|
if (g_str_equal(str, "radial"))
|
||||||
return GRADIENT_CENTERED;
|
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;
|
return GRADIENT_VERTICAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+1
-2
@@ -67,11 +67,10 @@ void cleanup_gradient(GradientClass *g);
|
|||||||
// Gradient instances associated to Areas
|
// Gradient instances associated to Areas
|
||||||
|
|
||||||
struct Area;
|
struct Area;
|
||||||
typedef struct Area Area;
|
|
||||||
|
|
||||||
typedef struct GradientInstance {
|
typedef struct GradientInstance {
|
||||||
GradientClass *gradient_class;
|
GradientClass *gradient_class;
|
||||||
Area *area;
|
struct Area *area;
|
||||||
cairo_pattern_t *pattern;
|
cairo_pattern_t *pattern;
|
||||||
} GradientInstance;
|
} GradientInstance;
|
||||||
|
|
||||||
|
|||||||
+471
@@ -0,0 +1,471 @@
|
|||||||
|
#ifdef ENABLE_EXECINFO
|
||||||
|
|
||||||
|
#include <dlfcn.h>
|
||||||
|
#include <execinfo.h>
|
||||||
|
#include <pthread.h>
|
||||||
|
#include <stdc-predef.h>
|
||||||
|
#include <stddef.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <sys/time.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <zlib.h>
|
||||||
|
|
||||||
|
#include "addr2line.h"
|
||||||
|
#include "mem.h"
|
||||||
|
#include "print.h"
|
||||||
|
|
||||||
|
#ifndef RTLD_NEXT
|
||||||
|
# define RTLD_NEXT ((void *) -1l)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef thread_local
|
||||||
|
# if __STDC_VERSION__ >= 201112 && !defined __STDC_NO_THREADS__
|
||||||
|
# define thread_local _Thread_local
|
||||||
|
# elif defined _WIN32 && ( \
|
||||||
|
defined _MSC_VER || \
|
||||||
|
defined __ICL || \
|
||||||
|
defined __DMC__ || \
|
||||||
|
defined __BORLANDC__ )
|
||||||
|
# define thread_local __declspec(thread)
|
||||||
|
/* note that ICC (linux) and Clang are covered by __GNUC__ */
|
||||||
|
# elif defined __GNUC__ || \
|
||||||
|
defined __SUNPRO_C || \
|
||||||
|
defined __xlC__
|
||||||
|
# define thread_local __thread
|
||||||
|
# else
|
||||||
|
# error "Cannot define thread_local"
|
||||||
|
# endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define get_caller() __builtin_extract_return_addr(__builtin_return_address(0))
|
||||||
|
|
||||||
|
#define size_t_mul_overflow(a, b) (a > 0 && b > SIZE_MAX / a)
|
||||||
|
|
||||||
|
typedef struct ListItem {
|
||||||
|
struct ListItem *next;
|
||||||
|
struct ListItem *prev;
|
||||||
|
void *data;
|
||||||
|
} ListItem;
|
||||||
|
|
||||||
|
typedef struct List {
|
||||||
|
ListItem *head;
|
||||||
|
ListItem *tail;
|
||||||
|
} List;
|
||||||
|
|
||||||
|
void list_append(List *list, void *data)
|
||||||
|
{
|
||||||
|
ListItem *n = (ListItem*)calloc(1, sizeof(ListItem));
|
||||||
|
n->data = data;
|
||||||
|
if (!list->tail) {
|
||||||
|
list->head = list->tail = n;
|
||||||
|
} else {
|
||||||
|
list->tail->next = n;
|
||||||
|
n->prev = list->tail;
|
||||||
|
list->tail = list->tail->next;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
typedef size_t (*HashFunc)(void *data);
|
||||||
|
typedef int (*EqualFunc)(void *data1, void *data2);
|
||||||
|
typedef void *(*CopyFunc)(void *data);
|
||||||
|
|
||||||
|
typedef struct HashTable {
|
||||||
|
List *buckets;
|
||||||
|
size_t num_buckets;
|
||||||
|
size_t (*hash_func)(void *data);
|
||||||
|
int (*equal_func)(void *data1, void *data2);
|
||||||
|
void *(*copy_func)(void *data);
|
||||||
|
} HashTable;
|
||||||
|
|
||||||
|
size_t hash_void(void *data)
|
||||||
|
{
|
||||||
|
return (size_t)data;
|
||||||
|
}
|
||||||
|
|
||||||
|
int equal_void(void *data1, void *data2)
|
||||||
|
{
|
||||||
|
return data1 == data2;
|
||||||
|
}
|
||||||
|
|
||||||
|
void *copy_void(void *data)
|
||||||
|
{
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
void hash_table_init(HashTable *table, size_t num_buckets, HashFunc hash_func, EqualFunc equal_func, CopyFunc copy_func)
|
||||||
|
{
|
||||||
|
if (table->buckets)
|
||||||
|
return;
|
||||||
|
if (num_buckets < 1)
|
||||||
|
num_buckets = 4096;
|
||||||
|
table->num_buckets = num_buckets;
|
||||||
|
table->buckets = (List*)calloc(table->num_buckets, sizeof(List));
|
||||||
|
table->hash_func = hash_func ? hash_func : hash_void;
|
||||||
|
table->equal_func = equal_func ? equal_func : equal_void;
|
||||||
|
table->copy_func = copy_func ? copy_func : copy_void;
|
||||||
|
}
|
||||||
|
|
||||||
|
void *hash_table_add(HashTable *table, void *data)
|
||||||
|
{
|
||||||
|
size_t hash = table->hash_func(data);
|
||||||
|
size_t bucket = hash % table->num_buckets;
|
||||||
|
for (ListItem *existing = table->buckets[bucket].head; existing; existing = existing->next) {
|
||||||
|
if (table->equal_func(existing->data, data))
|
||||||
|
return existing->data;
|
||||||
|
}
|
||||||
|
list_append(&table->buckets[bucket], table->copy_func(data));
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
#define MAX_TRACE_SIZE 120
|
||||||
|
|
||||||
|
typedef struct Trace {
|
||||||
|
void *entries[MAX_TRACE_SIZE+8];
|
||||||
|
size_t id;
|
||||||
|
} Trace;
|
||||||
|
|
||||||
|
#define LARGE_THRESH (10 * 1024 * 1024)
|
||||||
|
|
||||||
|
static void *(*calloc_original)(size_t count, size_t size) = NULL;
|
||||||
|
static void *(*realloc_original)(void *p, size_t size) = NULL;
|
||||||
|
static void *(*malloc_original)(size_t size) = NULL;
|
||||||
|
static void *(*free_original)(void *p) = NULL;
|
||||||
|
static gzFile mem_log = NULL;
|
||||||
|
static pid_t mem_log_owner = 0;
|
||||||
|
pthread_mutex_t log_mutex = PTHREAD_MUTEX_INITIALIZER;
|
||||||
|
|
||||||
|
static HashTable traces;
|
||||||
|
static size_t next_trace_id = 0;
|
||||||
|
pthread_mutex_t trace_mutex = PTHREAD_MUTEX_INITIALIZER;
|
||||||
|
|
||||||
|
static HashTable addr2lines;
|
||||||
|
pthread_mutex_t addr2lines_mutex = PTHREAD_MUTEX_INITIALIZER;
|
||||||
|
|
||||||
|
thread_local int paused = 0;
|
||||||
|
|
||||||
|
static double get_unix_time()
|
||||||
|
{
|
||||||
|
struct timeval tv;
|
||||||
|
if (gettimeofday(&tv, NULL) != 0)
|
||||||
|
return 0;
|
||||||
|
return tv.tv_sec + 1.0e-6 * tv.tv_usec;
|
||||||
|
}
|
||||||
|
|
||||||
|
__attribute__ ((noinline))
|
||||||
|
static void get_backtrace(Trace *trace) {
|
||||||
|
int size = backtrace(trace->entries, MAX_TRACE_SIZE);
|
||||||
|
trace->entries[size] = NULL;
|
||||||
|
for (void **p = trace->entries; *p; p++) {
|
||||||
|
*p = *(p+2);
|
||||||
|
}
|
||||||
|
trace->id = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int trace_size(Trace *trace)
|
||||||
|
{
|
||||||
|
int size = 0;
|
||||||
|
for (void **p = trace->entries; *p; p++) {
|
||||||
|
size++;
|
||||||
|
}
|
||||||
|
return size;
|
||||||
|
}
|
||||||
|
|
||||||
|
typedef struct FilenameLookup {
|
||||||
|
char *file_name;
|
||||||
|
Lookup *lookup;
|
||||||
|
} FilenameLookup;
|
||||||
|
|
||||||
|
size_t hash_lookup(void *data)
|
||||||
|
{
|
||||||
|
FilenameLookup *lookup = (FilenameLookup *)data;
|
||||||
|
size_t seed = 14695981039346656037ULL;
|
||||||
|
size_t hash = seed;
|
||||||
|
for (char *p = lookup->file_name; *p; p++) {
|
||||||
|
hash = (hash ^ (size_t)(*p)) * 1099511628211ULL;
|
||||||
|
}
|
||||||
|
return hash;
|
||||||
|
}
|
||||||
|
|
||||||
|
int equal_lookup(void *data1, void *data2)
|
||||||
|
{
|
||||||
|
FilenameLookup *lookup1 = (FilenameLookup *)data1;
|
||||||
|
FilenameLookup *lookup2 = (FilenameLookup *)data2;
|
||||||
|
return strcmp(lookup1->file_name, lookup2->file_name) == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
char *resolve_symbol(char *file_name, void *address)
|
||||||
|
{
|
||||||
|
FilenameLookup *lookup = (FilenameLookup *)calloc(1, sizeof(FilenameLookup));
|
||||||
|
lookup->file_name = strdup(file_name);
|
||||||
|
|
||||||
|
pthread_mutex_lock(&addr2lines_mutex);
|
||||||
|
hash_table_init(&addr2lines, 0, hash_lookup, equal_lookup, NULL);
|
||||||
|
FilenameLookup *existing = (FilenameLookup *)hash_table_add(&addr2lines, lookup);
|
||||||
|
if (existing == lookup) {
|
||||||
|
lookup->lookup = addr2line_init(lookup->file_name);
|
||||||
|
} else {
|
||||||
|
free(lookup->file_name);
|
||||||
|
free(lookup);
|
||||||
|
}
|
||||||
|
char *result = addr2line_lookup(existing->lookup, address);
|
||||||
|
pthread_mutex_unlock(&addr2lines_mutex);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
char *backtrace_to_string(Trace *trace)
|
||||||
|
{
|
||||||
|
int size = trace_size(trace);
|
||||||
|
char **strings = backtrace_symbols(trace->entries, size);
|
||||||
|
if (!strings)
|
||||||
|
return NULL;
|
||||||
|
Buffer *buffer = NULL;
|
||||||
|
buffer = buffer_printf(buffer, "t %lu ", trace->id);
|
||||||
|
for (int i = 0; i < size; i++) {
|
||||||
|
buffer = buffer_printf(buffer, "%s", strings[i]);
|
||||||
|
// /lib/x86_64-linux-gnu/libglib-2.0.so.0(g_realloc+0xf) [0x7efda85c96af]
|
||||||
|
char *func_start = strstr(strings[i], "(");
|
||||||
|
if (func_start) {
|
||||||
|
*func_start = 0;
|
||||||
|
char *file_name = strings[i];
|
||||||
|
char *resolved = resolve_symbol(file_name, trace->entries[i]);
|
||||||
|
if (resolved) {
|
||||||
|
buffer = buffer_printf(buffer, " %s", resolved);
|
||||||
|
free(resolved);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
buffer = buffer_printf(buffer, "|");
|
||||||
|
}
|
||||||
|
free(strings);
|
||||||
|
buffer = buffer_printf(buffer, "\n");
|
||||||
|
return buffer->data;
|
||||||
|
}
|
||||||
|
|
||||||
|
static size_t hash_trace(Trace *trace)
|
||||||
|
{
|
||||||
|
size_t seed = 14695981039346656037ULL;
|
||||||
|
size_t hash = seed;
|
||||||
|
for (void **p = trace->entries; *p; p++) {
|
||||||
|
hash = (hash ^ (size_t)(*p)) * 1099511628211ULL;
|
||||||
|
}
|
||||||
|
return hash;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int equal_traces(Trace *a, Trace *b)
|
||||||
|
{
|
||||||
|
void **pa, **pb;
|
||||||
|
for (pa = a->entries, pb = b->entries; *pa && *pb; pa++, pb++) {
|
||||||
|
if (*pa != *pb)
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
if (*pa || *pb)
|
||||||
|
return 0;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static Trace *copy_trace(Trace *trace)
|
||||||
|
{
|
||||||
|
Trace *result = (Trace*)calloc(1, sizeof(Trace));
|
||||||
|
*result = *trace;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int trace_assign_id(Trace *trace)
|
||||||
|
{
|
||||||
|
int created = 0;
|
||||||
|
pthread_mutex_lock(&trace_mutex);
|
||||||
|
hash_table_init(&traces, 1024 * 1024, (HashFunc)hash_trace, (EqualFunc)equal_traces, (CopyFunc)copy_trace);
|
||||||
|
trace->id = 0;
|
||||||
|
Trace *existing = (Trace*)hash_table_add(&traces, trace);
|
||||||
|
if (!existing->id) {
|
||||||
|
next_trace_id++;
|
||||||
|
existing->id = trace->id = next_trace_id;
|
||||||
|
created = 1;
|
||||||
|
} else {
|
||||||
|
trace->id = existing->id;
|
||||||
|
}
|
||||||
|
pthread_mutex_unlock(&trace_mutex);
|
||||||
|
return created;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void log_flush();
|
||||||
|
|
||||||
|
static void log_init()
|
||||||
|
{
|
||||||
|
char path[256];
|
||||||
|
sprintf(path, "/tmp/tint2.%lu.memtrace", (size_t)getpid());
|
||||||
|
pthread_mutex_lock(&log_mutex);
|
||||||
|
if (!mem_log) {
|
||||||
|
mem_log = gzopen(path, "w");
|
||||||
|
mem_log_owner = getpid();
|
||||||
|
atexit(log_flush);
|
||||||
|
}
|
||||||
|
pthread_mutex_unlock(&log_mutex);
|
||||||
|
if (mem_log)
|
||||||
|
fprintf(stderr, "Writing memory log to %s\n", path);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void log_write(char *buffer)
|
||||||
|
{
|
||||||
|
if (mem_log) {
|
||||||
|
pthread_mutex_lock(&log_mutex);
|
||||||
|
if (getpid() == mem_log_owner)
|
||||||
|
gzwrite(mem_log, buffer, (unsigned)strlen(buffer));
|
||||||
|
pthread_mutex_unlock(&log_mutex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void log_flush()
|
||||||
|
{
|
||||||
|
if (mem_log) {
|
||||||
|
pthread_mutex_lock(&log_mutex);
|
||||||
|
if (getpid() == mem_log_owner)
|
||||||
|
gzflush(mem_log, Z_SYNC_FLUSH);
|
||||||
|
pthread_mutex_unlock(&log_mutex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void malloc_profile_load_pointers()
|
||||||
|
{
|
||||||
|
realloc_original = dlsym(RTLD_NEXT, "realloc");
|
||||||
|
malloc_original = dlsym(RTLD_NEXT, "malloc");
|
||||||
|
calloc_original = dlsym(RTLD_NEXT, "calloc");
|
||||||
|
free_original = dlsym(RTLD_NEXT, "free");
|
||||||
|
int trace_mem = getenv("TRACE_MEM") != NULL;
|
||||||
|
if (trace_mem)
|
||||||
|
log_init();
|
||||||
|
else
|
||||||
|
paused = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
void *calloc(size_t count, size_t size)
|
||||||
|
{
|
||||||
|
if (paused)
|
||||||
|
return calloc_original ? calloc_original(count, size) : 0;
|
||||||
|
paused = 1;
|
||||||
|
if (!calloc_original) {
|
||||||
|
malloc_profile_load_pointers();
|
||||||
|
if (!calloc_original)
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
void *result = calloc_original(count, size);
|
||||||
|
if (mem_log) {
|
||||||
|
Trace trace;
|
||||||
|
get_backtrace(&trace);
|
||||||
|
int created = trace_assign_id(&trace);
|
||||||
|
if (created) {
|
||||||
|
char *str = backtrace_to_string(&trace);
|
||||||
|
if (str) {
|
||||||
|
log_write(str);
|
||||||
|
free(str);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
char buffer[1024];
|
||||||
|
sprintf(buffer, "[%f] t %lu : %p = c %lu %lu\n", get_unix_time(), trace.id, result, count, size);
|
||||||
|
log_write(buffer);
|
||||||
|
if (size_t_mul_overflow(count, size) || (count * size) > LARGE_THRESH)
|
||||||
|
log_flush();
|
||||||
|
}
|
||||||
|
paused = 0;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
void *realloc(void *p, size_t size)
|
||||||
|
{
|
||||||
|
if (paused)
|
||||||
|
return realloc_original ? realloc_original(p, size) : 0;
|
||||||
|
paused = 1;
|
||||||
|
if (!realloc_original) {
|
||||||
|
malloc_profile_load_pointers();
|
||||||
|
if (!realloc_original)
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
void *result = realloc_original(p, size);
|
||||||
|
if (mem_log) {
|
||||||
|
Trace trace;
|
||||||
|
get_backtrace(&trace);
|
||||||
|
int created = trace_assign_id(&trace);
|
||||||
|
if (created) {
|
||||||
|
char *str = backtrace_to_string(&trace);
|
||||||
|
if (str) {
|
||||||
|
log_write(str);
|
||||||
|
free(str);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
char buffer[1024];
|
||||||
|
sprintf(buffer, "[%f] t %lu : %p = r %p %lu\n", get_unix_time(), trace.id, result, p, size);
|
||||||
|
log_write(buffer);
|
||||||
|
if (size > LARGE_THRESH)
|
||||||
|
log_flush();
|
||||||
|
}
|
||||||
|
paused = 0;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
void *malloc(size_t size)
|
||||||
|
{
|
||||||
|
if (paused)
|
||||||
|
return malloc_original ? malloc_original(size) : 0;
|
||||||
|
paused = 1;
|
||||||
|
if (!malloc_original) {
|
||||||
|
malloc_profile_load_pointers();
|
||||||
|
if (!malloc_original)
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
void *result = malloc_original(size);
|
||||||
|
if (mem_log) {
|
||||||
|
Trace trace;
|
||||||
|
get_backtrace(&trace);
|
||||||
|
int created = trace_assign_id(&trace);
|
||||||
|
if (created) {
|
||||||
|
char *str = backtrace_to_string(&trace);
|
||||||
|
if (str) {
|
||||||
|
log_write(str);
|
||||||
|
free(str);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
char buffer[1024];
|
||||||
|
sprintf(buffer, "[%f] t %lu : %p = m %lu\n", get_unix_time(), trace.id, result, size);
|
||||||
|
log_write(buffer);
|
||||||
|
if (size > LARGE_THRESH)
|
||||||
|
log_flush();
|
||||||
|
}
|
||||||
|
paused = 0;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
void free(void *p)
|
||||||
|
{
|
||||||
|
if (paused) {
|
||||||
|
if (free_original)
|
||||||
|
free_original(p);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
paused = 1;
|
||||||
|
if (!free_original) {
|
||||||
|
malloc_profile_load_pointers();
|
||||||
|
if (!free_original)
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
free_original(p);
|
||||||
|
if (mem_log) {
|
||||||
|
Trace trace;
|
||||||
|
get_backtrace(&trace);
|
||||||
|
int created = trace_assign_id(&trace);
|
||||||
|
if (created) {
|
||||||
|
char *str = backtrace_to_string(&trace);
|
||||||
|
if (str) {
|
||||||
|
log_write(str);
|
||||||
|
free(str);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
char buffer[1024];
|
||||||
|
sprintf(buffer, "[%f] t %lu : 0 = f %p\n", get_unix_time(), trace.id, p);
|
||||||
|
log_write(buffer);
|
||||||
|
}
|
||||||
|
paused = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
#ifndef MEM_H
|
||||||
|
#define MEM_H
|
||||||
|
|
||||||
|
#endif
|
||||||
@@ -0,0 +1,40 @@
|
|||||||
|
#include <stdarg.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include "print.h"
|
||||||
|
|
||||||
|
Buffer *vbuffer_printf(Buffer *buffer, const char *fmt, va_list ap)
|
||||||
|
{
|
||||||
|
va_list ap1;
|
||||||
|
va_copy(ap1, ap);
|
||||||
|
int ret = vsnprintf(NULL, 0, fmt, ap1) + 1;
|
||||||
|
va_end(ap1);
|
||||||
|
if (ret < 1)
|
||||||
|
return buffer;
|
||||||
|
|
||||||
|
size_t size = (size_t)ret;
|
||||||
|
if (!buffer)
|
||||||
|
buffer = (Buffer*)calloc(1, sizeof(Buffer));
|
||||||
|
if (!buffer->data) {
|
||||||
|
buffer->size = 2 * size + 128;
|
||||||
|
buffer->data = (char*) calloc(buffer->size, 1);
|
||||||
|
} else if (strlen(buffer->data) + size >= buffer->size + 1) {
|
||||||
|
buffer->size = 2 * (size + buffer->size) + 128;
|
||||||
|
buffer->data = (char*) realloc(buffer->data, buffer->size);
|
||||||
|
}
|
||||||
|
|
||||||
|
vsnprintf(buffer->data + strlen(buffer->data), size, fmt, ap);
|
||||||
|
return buffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
Buffer *buffer_printf(Buffer *buffer, const char *fmt, ...)
|
||||||
|
{
|
||||||
|
va_list ap;
|
||||||
|
|
||||||
|
va_start(ap, fmt);
|
||||||
|
buffer = vbuffer_printf(buffer, fmt, ap);
|
||||||
|
va_end(ap);
|
||||||
|
|
||||||
|
return buffer;
|
||||||
|
}
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
#ifndef PRINT_H
|
||||||
|
#define PRINT_H
|
||||||
|
|
||||||
|
#include <stdarg.h>
|
||||||
|
#include <stddef.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
|
||||||
|
typedef struct Buffer {
|
||||||
|
char *data;
|
||||||
|
size_t size;
|
||||||
|
} Buffer;
|
||||||
|
|
||||||
|
Buffer *vbuffer_printf(Buffer *buffer, const char *fmt, va_list ap);
|
||||||
|
Buffer *buffer_printf(Buffer *buffer, const char *fmt, ...);
|
||||||
|
|
||||||
|
#endif
|
||||||
+22
-12
@@ -44,6 +44,9 @@ struct _timeout {
|
|||||||
void *arg;
|
void *arg;
|
||||||
multi_timeout *multi_timeout;
|
multi_timeout *multi_timeout;
|
||||||
timeout **self;
|
timeout **self;
|
||||||
|
gboolean expired;
|
||||||
|
// timer has been restarted from its own callback function
|
||||||
|
gboolean reactivated;
|
||||||
};
|
};
|
||||||
|
|
||||||
void add_timeout_intern(int value_msec, int interval_msec, void (*_callback)(void *), void *arg, timeout *t);
|
void add_timeout_intern(int value_msec, int interval_msec, void (*_callback)(void *), void *arg, timeout *t);
|
||||||
@@ -106,7 +109,7 @@ int gettime(struct timespec *tp)
|
|||||||
|
|
||||||
timeout *add_timeout(int value_msec, int interval_msec, void (*_callback)(void *), void *arg, timeout **self)
|
timeout *add_timeout(int value_msec, int interval_msec, void (*_callback)(void *), void *arg, timeout **self)
|
||||||
{
|
{
|
||||||
if (self && *self)
|
if (self && *self && !(*self)->expired)
|
||||||
return *self;
|
return *self;
|
||||||
timeout *t = calloc(1, sizeof(timeout));
|
timeout *t = calloc(1, sizeof(timeout));
|
||||||
t->self = self;
|
t->self = self;
|
||||||
@@ -128,7 +131,7 @@ void change_timeout(timeout **t, int value_msec, int interval_msec, void (*_call
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void update_next_timeout()
|
struct timeval *get_next_timeout()
|
||||||
{
|
{
|
||||||
if (timeout_list) {
|
if (timeout_list) {
|
||||||
timeout *t = timeout_list->data;
|
timeout *t = timeout_list->data;
|
||||||
@@ -144,9 +147,10 @@ void update_next_timeout()
|
|||||||
}
|
}
|
||||||
} else
|
} else
|
||||||
next_timeout.tv_sec = -1;
|
next_timeout.tv_sec = -1;
|
||||||
|
return (next_timeout.tv_sec >= 0 && next_timeout.tv_usec >= 0) ? &next_timeout : NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
void callback_timeout_expired()
|
void handle_expired_timers()
|
||||||
{
|
{
|
||||||
struct timespec cur_time;
|
struct timespec cur_time;
|
||||||
timeout *t;
|
timeout *t;
|
||||||
@@ -155,18 +159,22 @@ void callback_timeout_expired()
|
|||||||
t = timeout_list->data;
|
t = timeout_list->data;
|
||||||
if (compare_timespecs(&t->timeout_expires, &cur_time) <= 0) {
|
if (compare_timespecs(&t->timeout_expires, &cur_time) <= 0) {
|
||||||
// it's time for the callback function
|
// it's time for the callback function
|
||||||
|
t->expired = t->interval_msec == 0;
|
||||||
|
t->reactivated = FALSE;
|
||||||
t->_callback(t->arg);
|
t->_callback(t->arg);
|
||||||
// If _callback() calls stop_timeout(t) the timer 't' was freed and is not in the timeout_list
|
// If _callback() calls stop_timeout(t) the timer 't' was freed and is not in the timeout_list
|
||||||
if (g_slist_find(timeout_list, t)) {
|
if (g_slist_find(timeout_list, t)) {
|
||||||
// Timer still exists
|
// Timer still exists
|
||||||
timeout_list = g_slist_remove(timeout_list, t);
|
if (!t->reactivated) {
|
||||||
if (t->interval_msec > 0) {
|
timeout_list = g_slist_remove(timeout_list, t);
|
||||||
add_timeout_intern(t->interval_msec, t->interval_msec, t->_callback, t->arg, t);
|
if (t->interval_msec > 0) {
|
||||||
} else {
|
add_timeout_intern(t->interval_msec, t->interval_msec, t->_callback, t->arg, t);
|
||||||
// Destroy single-shot timer
|
} else {
|
||||||
if (t->self)
|
// Destroy single-shot timer
|
||||||
*t->self = NULL;
|
if (t->self)
|
||||||
free(t);
|
*t->self = NULL;
|
||||||
|
free(t);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@@ -205,6 +213,7 @@ void add_timeout_intern(int value_msec, int interval_msec, void (*_callback)(),
|
|||||||
can_align = align_with_existing_timeouts(t);
|
can_align = align_with_existing_timeouts(t);
|
||||||
if (!can_align)
|
if (!can_align)
|
||||||
timeout_list = g_slist_insert_sorted(timeout_list, t, compare_timeouts);
|
timeout_list = g_slist_insert_sorted(timeout_list, t, compare_timeouts);
|
||||||
|
t->reactivated = TRUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
gint compare_timeouts(gconstpointer t1, gconstpointer t2)
|
gint compare_timeouts(gconstpointer t1, gconstpointer t2)
|
||||||
@@ -381,6 +390,7 @@ void callback_multi_timeout(void *arg)
|
|||||||
gettime(&cur_time);
|
gettime(&cur_time);
|
||||||
GSList *it = mth->timeout_list;
|
GSList *it = mth->timeout_list;
|
||||||
while (it) {
|
while (it) {
|
||||||
|
GSList *next = it->next;
|
||||||
timeout *t = it->data;
|
timeout *t = it->data;
|
||||||
if (++t->multi_timeout->current_count >= t->multi_timeout->count_to_expiration) {
|
if (++t->multi_timeout->current_count >= t->multi_timeout->count_to_expiration) {
|
||||||
t->_callback(t->arg);
|
t->_callback(t->arg);
|
||||||
@@ -392,7 +402,7 @@ void callback_multi_timeout(void *arg)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
it = it->next;
|
it = next;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+4
-4
@@ -30,7 +30,6 @@
|
|||||||
// Periodic timeouts are aligned to each other whenever possible, i.e. one interval_msec is an
|
// Periodic timeouts are aligned to each other whenever possible, i.e. one interval_msec is an
|
||||||
// integral multiple of the other.
|
// integral multiple of the other.
|
||||||
|
|
||||||
extern struct timeval next_timeout;
|
|
||||||
typedef struct _timeout timeout;
|
typedef struct _timeout timeout;
|
||||||
|
|
||||||
// Initializes default global data.
|
// Initializes default global data.
|
||||||
@@ -54,11 +53,12 @@ void change_timeout(timeout **t, int value_msec, int interval_msec, void (*_call
|
|||||||
// Stops the timer 't'
|
// Stops the timer 't'
|
||||||
void stop_timeout(timeout *t);
|
void stop_timeout(timeout *t);
|
||||||
|
|
||||||
// Updates next_timeout to the value, when the next installed timeout will expire
|
// Get the time when the next installed timer will expire, or NULL if there is no timer.
|
||||||
void update_next_timeout();
|
// Do not free the pointer; but it is safe to change its contents.
|
||||||
|
struct timeval *get_next_timeout();
|
||||||
|
|
||||||
// Callback of all expired timeouts
|
// Callback of all expired timeouts
|
||||||
void callback_timeout_expired();
|
void handle_expired_timers();
|
||||||
|
|
||||||
// Returns -1 if t1 < t2, 0 if t1 == t2, 1 if t1 > t2
|
// Returns -1 if t1 < t2, 0 if t1 == t2, 1 if t1 > t2
|
||||||
gint compare_timespecs(const struct timespec *t1, const struct timespec *t2);
|
gint compare_timespecs(const struct timespec *t1, const struct timespec *t2);
|
||||||
|
|||||||
+14
-13
@@ -17,6 +17,9 @@
|
|||||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||||
**************************************************************************/
|
**************************************************************************/
|
||||||
|
|
||||||
|
#include "uevent.h"
|
||||||
|
int uevent_fd = -1;
|
||||||
|
|
||||||
#ifdef ENABLE_UEVENT
|
#ifdef ENABLE_UEVENT
|
||||||
|
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
@@ -30,9 +33,7 @@
|
|||||||
#include <linux/netlink.h>
|
#include <linux/netlink.h>
|
||||||
|
|
||||||
#include "common.h"
|
#include "common.h"
|
||||||
#include "uevent.h"
|
|
||||||
|
|
||||||
static int ueventfd = -1;
|
|
||||||
static struct sockaddr_nl nls;
|
static struct sockaddr_nl nls;
|
||||||
static GList *notifiers = NULL;
|
static GList *notifiers = NULL;
|
||||||
|
|
||||||
@@ -146,11 +147,11 @@ void uevent_unregister_notifier(struct uevent_notify *nb)
|
|||||||
|
|
||||||
void uevent_handler()
|
void uevent_handler()
|
||||||
{
|
{
|
||||||
if (ueventfd < 0)
|
if (uevent_fd < 0)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
char buf[512];
|
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)
|
if (len < 0)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
@@ -181,27 +182,27 @@ int uevent_init()
|
|||||||
nls.nl_groups = -1;
|
nls.nl_groups = -1;
|
||||||
|
|
||||||
/* open socket */
|
/* open socket */
|
||||||
ueventfd = socket(PF_NETLINK, SOCK_DGRAM, NETLINK_KOBJECT_UEVENT);
|
uevent_fd = socket(PF_NETLINK, SOCK_DGRAM, NETLINK_KOBJECT_UEVENT);
|
||||||
if (ueventfd < 0) {
|
if (uevent_fd < 0) {
|
||||||
fprintf(stderr, "Error: socket open failed\n");
|
fprintf(stderr, "tint2: Error: socket open failed\n");
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Listen to netlink socket */
|
/* Listen to netlink socket */
|
||||||
if (bind(ueventfd, (void *)&nls, sizeof(struct sockaddr_nl))) {
|
if (bind(uevent_fd, (void *)&nls, sizeof(struct sockaddr_nl))) {
|
||||||
fprintf(stderr, "Bind failed\n");
|
fprintf(stderr, "tint2: Bind failed\n");
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
printf("Kernel uevent interface initialized...\n");
|
fprintf(stderr, "tint2: Kernel uevent interface initialized...\n");
|
||||||
|
|
||||||
return ueventfd;
|
return uevent_fd;
|
||||||
}
|
}
|
||||||
|
|
||||||
void uevent_cleanup()
|
void uevent_cleanup()
|
||||||
{
|
{
|
||||||
if (ueventfd >= 0)
|
if (uevent_fd >= 0)
|
||||||
close(ueventfd);
|
close(uevent_fd);
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -20,6 +20,8 @@
|
|||||||
#ifndef UEVENT_H
|
#ifndef UEVENT_H
|
||||||
#define UEVENT_H
|
#define UEVENT_H
|
||||||
|
|
||||||
|
#include <glib.h>
|
||||||
|
|
||||||
enum uevent_action {
|
enum uevent_action {
|
||||||
UEVENT_UNKNOWN = 0x01,
|
UEVENT_UNKNOWN = 0x01,
|
||||||
UEVENT_ADD = 0x02,
|
UEVENT_ADD = 0x02,
|
||||||
@@ -48,6 +50,8 @@ struct uevent_notify {
|
|||||||
void (*cb)(struct uevent *e, void *userdata);
|
void (*cb)(struct uevent *e, void *userdata);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
extern int uevent_fd;
|
||||||
|
|
||||||
#if ENABLE_UEVENT
|
#if ENABLE_UEVENT
|
||||||
int uevent_init();
|
int uevent_init();
|
||||||
void uevent_cleanup();
|
void uevent_cleanup();
|
||||||
|
|||||||
+264
-6
@@ -24,11 +24,17 @@
|
|||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
|
||||||
#include <Imlib2.h>
|
#include <Imlib2.h>
|
||||||
#include <cairo.h>
|
#include <cairo.h>
|
||||||
#include <cairo-xlib.h>
|
#include <cairo-xlib.h>
|
||||||
|
|
||||||
|
#include <X11/extensions/XShm.h>
|
||||||
|
#include <sys/ipc.h>
|
||||||
|
#include <sys/shm.h>
|
||||||
|
|
||||||
#include "common.h"
|
#include "common.h"
|
||||||
#include "window.h"
|
#include "window.h"
|
||||||
#include "server.h"
|
#include "server.h"
|
||||||
@@ -157,7 +163,8 @@ int get_window_desktop(Window win)
|
|||||||
|
|
||||||
if (best_match < 0)
|
if (best_match < 0)
|
||||||
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);
|
// best_match+1, x, y);
|
||||||
return best_match;
|
return best_match;
|
||||||
}
|
}
|
||||||
@@ -185,20 +192,23 @@ int get_window_monitor(Window win)
|
|||||||
|
|
||||||
if (best_match < 0)
|
if (best_match < 0)
|
||||||
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);
|
// get_task(win) ? get_task(win)->title : "??", best_match+1, x, y);
|
||||||
return best_match;
|
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;
|
int dummy_int;
|
||||||
unsigned ww, wh, bw, bh;
|
unsigned ww, wh, bw, bh;
|
||||||
Window src;
|
Window src;
|
||||||
XTranslateCoordinates(server.display, win, server.root_win, 0, 0, x, y, &src);
|
if (!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);
|
return FALSE;
|
||||||
|
if (!XGetGeometry(server.display, win, &src, &dummy_int, &dummy_int, &ww, &wh, &bw, &bh))
|
||||||
|
return FALSE;
|
||||||
*w = ww + bw;
|
*w = ww + bw;
|
||||||
*h = wh + bh;
|
*h = wh + bw;
|
||||||
|
return TRUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
gboolean window_is_iconified(Window win)
|
gboolean window_is_iconified(Window win)
|
||||||
@@ -352,3 +362,251 @@ char *get_window_name(Window win)
|
|||||||
XFree(text_property.value);
|
XFree(text_property.value);
|
||||||
return result;
|
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;
|
||||||
|
}
|
||||||
|
|||||||
+2
-1
@@ -25,7 +25,7 @@ int get_window_monitor(Window win);
|
|||||||
|
|
||||||
void activate_window(Window win);
|
void activate_window(Window win);
|
||||||
void close_window(Window win);
|
void close_window(Window win);
|
||||||
void get_window_coordinates(Window win, int *x, int *y, int *w, int *h);
|
gboolean get_window_coordinates(Window win, int *x, int *y, int *w, int *h);
|
||||||
void toggle_window_maximized(Window win);
|
void toggle_window_maximized(Window win);
|
||||||
void toggle_window_shade(Window win);
|
void toggle_window_shade(Window win);
|
||||||
void change_window_desktop(Window win, int desktop);
|
void change_window_desktop(Window win, int desktop);
|
||||||
@@ -34,5 +34,6 @@ int get_icon_count(gulong *data, int num);
|
|||||||
gulong *get_best_icon(gulong *data, int icon_count, int num, int *iw, int *ih, int best_icon_size);
|
gulong *get_best_icon(gulong *data, int icon_count, int num, int *iw, int *ih, int best_icon_size);
|
||||||
|
|
||||||
char *get_window_name(Window win);
|
char *get_window_name(Window win);
|
||||||
|
cairo_surface_t *get_window_thumbnail(Window win, int size);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -0,0 +1,130 @@
|
|||||||
|
AC0/type:Mains
|
||||||
|
AC0/power/control:auto
|
||||||
|
AC0/power/wakeup_prevent_sleep_time_ms:0
|
||||||
|
AC0/power/async:disabled
|
||||||
|
AC0/power/wakeup_abort_count:0
|
||||||
|
AC0/power/wakeup_active:0
|
||||||
|
AC0/power/wakeup_total_time_ms:0
|
||||||
|
AC0/power/wakeup_active_count:4
|
||||||
|
AC0/power/runtime_enabled:disabled
|
||||||
|
AC0/power/runtime_active_kids:0
|
||||||
|
AC0/power/runtime_active_time:0
|
||||||
|
AC0/power/wakeup_max_time_ms:0
|
||||||
|
AC0/power/wakeup_count:4
|
||||||
|
AC0/power/wakeup_last_time_ms:18773802
|
||||||
|
AC0/power/wakeup:enabled
|
||||||
|
AC0/power/runtime_status:unsupported
|
||||||
|
AC0/power/runtime_usage:0
|
||||||
|
AC0/power/wakeup_expire_count:0
|
||||||
|
AC0/power/runtime_suspended_time:0
|
||||||
|
AC0/online:1
|
||||||
|
AC0/uevent:POWER_SUPPLY_NAME=AC0
|
||||||
|
AC0/uevent:POWER_SUPPLY_ONLINE=1
|
||||||
|
BAT0/temp:26
|
||||||
|
BAT0/type:Battery
|
||||||
|
BAT0/power/control:auto
|
||||||
|
BAT0/power/wakeup_prevent_sleep_time_ms:0
|
||||||
|
BAT0/power/async:disabled
|
||||||
|
BAT0/power/wakeup_abort_count:0
|
||||||
|
BAT0/power/wakeup_active:0
|
||||||
|
BAT0/power/wakeup_total_time_ms:0
|
||||||
|
BAT0/power/wakeup_active_count:4
|
||||||
|
BAT0/power/runtime_enabled:disabled
|
||||||
|
BAT0/power/runtime_active_kids:0
|
||||||
|
BAT0/power/runtime_active_time:0
|
||||||
|
BAT0/power/wakeup_max_time_ms:0
|
||||||
|
BAT0/power/wakeup_count:4
|
||||||
|
BAT0/power/wakeup_last_time_ms:18773802
|
||||||
|
BAT0/power/wakeup:enabled
|
||||||
|
BAT0/power/runtime_status:unsupported
|
||||||
|
BAT0/power/runtime_usage:0
|
||||||
|
BAT0/power/wakeup_expire_count:0
|
||||||
|
BAT0/power/runtime_suspended_time:0
|
||||||
|
BAT0/charge_full_design:100
|
||||||
|
BAT0/charge_now:17
|
||||||
|
BAT0/charge_full:100
|
||||||
|
BAT0/charge_type:Fast
|
||||||
|
BAT0/capacity:17
|
||||||
|
BAT0/health:Good
|
||||||
|
BAT0/capacity_level:Critical
|
||||||
|
BAT0/status:Charging
|
||||||
|
BAT0/voltage_now:3300
|
||||||
|
BAT0/uevent:POWER_SUPPLY_NAME=BAT0
|
||||||
|
BAT0/uevent:POWER_SUPPLY_STATUS=Charging
|
||||||
|
BAT0/uevent:POWER_SUPPLY_CHARGE_TYPE=Fast
|
||||||
|
BAT0/uevent:POWER_SUPPLY_HEALTH=Good
|
||||||
|
BAT0/uevent:POWER_SUPPLY_PRESENT=1
|
||||||
|
BAT0/uevent:POWER_SUPPLY_TECHNOLOGY=Li-ion
|
||||||
|
BAT0/uevent:POWER_SUPPLY_CHARGE_FULL_DESIGN=100
|
||||||
|
BAT0/uevent:POWER_SUPPLY_CHARGE_FULL=100
|
||||||
|
BAT0/uevent:POWER_SUPPLY_CHARGE_NOW=17
|
||||||
|
BAT0/uevent:POWER_SUPPLY_CAPACITY=17
|
||||||
|
BAT0/uevent:POWER_SUPPLY_CAPACITY_LEVEL=Critical
|
||||||
|
BAT0/uevent:POWER_SUPPLY_TIME_TO_EMPTY_AVG=612
|
||||||
|
BAT0/uevent:POWER_SUPPLY_TIME_TO_FULL_NOW=612
|
||||||
|
BAT0/uevent:POWER_SUPPLY_MODEL_NAME=Fake battery 1
|
||||||
|
BAT0/uevent:POWER_SUPPLY_MANUFACTURER=Linux
|
||||||
|
BAT0/uevent:POWER_SUPPLY_SERIAL_NUMBER=12345678
|
||||||
|
BAT0/uevent:POWER_SUPPLY_TEMP=26
|
||||||
|
BAT0/uevent:POWER_SUPPLY_VOLTAGE_NOW=3300
|
||||||
|
BAT0/model_name:Fake battery 1
|
||||||
|
BAT0/manufacturer:Linux
|
||||||
|
BAT0/technology:Li-ion
|
||||||
|
BAT0/time_to_full_now:612
|
||||||
|
BAT0/time_to_empty_avg:612
|
||||||
|
BAT0/serial_number:12345678
|
||||||
|
BAT0/present:1
|
||||||
|
BAT1/temp:26
|
||||||
|
BAT1/type:Battery
|
||||||
|
BAT1/power/control:auto
|
||||||
|
BAT1/power/wakeup_prevent_sleep_time_ms:0
|
||||||
|
BAT1/power/async:disabled
|
||||||
|
BAT1/power/wakeup_abort_count:0
|
||||||
|
BAT1/power/wakeup_active:0
|
||||||
|
BAT1/power/wakeup_total_time_ms:0
|
||||||
|
BAT1/power/wakeup_active_count:4
|
||||||
|
BAT1/power/runtime_enabled:disabled
|
||||||
|
BAT1/power/runtime_active_kids:0
|
||||||
|
BAT1/power/runtime_active_time:0
|
||||||
|
BAT1/power/wakeup_max_time_ms:0
|
||||||
|
BAT1/power/wakeup_count:4
|
||||||
|
BAT1/power/wakeup_last_time_ms:18773802
|
||||||
|
BAT1/power/wakeup:enabled
|
||||||
|
BAT1/power/runtime_status:unsupported
|
||||||
|
BAT1/power/runtime_usage:0
|
||||||
|
BAT1/power/wakeup_expire_count:0
|
||||||
|
BAT1/power/runtime_suspended_time:0
|
||||||
|
BAT1/charge_full_design:100
|
||||||
|
BAT1/charge_now:7
|
||||||
|
BAT1/charge_full:100
|
||||||
|
BAT1/charge_type:Fast
|
||||||
|
BAT1/capacity:7
|
||||||
|
BAT1/health:Good
|
||||||
|
BAT1/capacity_level:Low
|
||||||
|
BAT1/status:Charging
|
||||||
|
BAT1/voltage_now:3300
|
||||||
|
BAT1/uevent:POWER_SUPPLY_NAME=BAT1
|
||||||
|
BAT1/uevent:POWER_SUPPLY_STATUS=Charging
|
||||||
|
BAT1/uevent:POWER_SUPPLY_CHARGE_TYPE=Fast
|
||||||
|
BAT1/uevent:POWER_SUPPLY_HEALTH=Good
|
||||||
|
BAT1/uevent:POWER_SUPPLY_PRESENT=1
|
||||||
|
BAT1/uevent:POWER_SUPPLY_TECHNOLOGY=Li-ion
|
||||||
|
BAT1/uevent:POWER_SUPPLY_CHARGE_FULL_DESIGN=100
|
||||||
|
BAT1/uevent:POWER_SUPPLY_CHARGE_FULL=100
|
||||||
|
BAT1/uevent:POWER_SUPPLY_CHARGE_NOW=7
|
||||||
|
BAT1/uevent:POWER_SUPPLY_CAPACITY=7
|
||||||
|
BAT1/uevent:POWER_SUPPLY_CAPACITY_LEVEL=Low
|
||||||
|
BAT1/uevent:POWER_SUPPLY_TIME_TO_EMPTY_AVG=252
|
||||||
|
BAT1/uevent:POWER_SUPPLY_TIME_TO_FULL_NOW=252
|
||||||
|
BAT1/uevent:POWER_SUPPLY_MODEL_NAME=Fake battery 2
|
||||||
|
BAT1/uevent:POWER_SUPPLY_MANUFACTURER=Linux
|
||||||
|
BAT1/uevent:POWER_SUPPLY_SERIAL_NUMBER=12345678
|
||||||
|
BAT1/uevent:POWER_SUPPLY_TEMP=26
|
||||||
|
BAT1/uevent:POWER_SUPPLY_VOLTAGE_NOW=3300
|
||||||
|
BAT1/model_name:Fake battery 2
|
||||||
|
BAT1/manufacturer:Linux
|
||||||
|
BAT1/technology:Li-ion
|
||||||
|
BAT1/time_to_full_now:252
|
||||||
|
BAT1/time_to_empty_avg:252
|
||||||
|
BAT1/serial_number:12345678
|
||||||
|
BAT1/present:1
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
1
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
disabled
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
auto
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
0
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
0
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user