Compare commits
30
Commits
v16.4
...
thumb-xrender
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
36c8d41914 | ||
|
|
4dafea185f | ||
|
|
77890d463a | ||
|
|
e1211a929f | ||
|
|
00f0bbd354 | ||
|
|
15d2570a3e | ||
|
|
c76f056746 | ||
|
|
cacd4b8dad | ||
|
|
b539c0a1c0 | ||
|
|
970c597796 | ||
|
|
a82b9a1d7f | ||
|
|
89a6cadfcd | ||
|
|
fb9da655df | ||
|
|
cb137674e4 | ||
|
|
1054aac7ca | ||
|
|
81c7c65a9f | ||
|
|
044bad6c40 | ||
|
|
aaa0e40af7 | ||
|
|
34f9fcaca4 | ||
|
|
f8c6dc1ecc | ||
|
|
11c468816d | ||
|
|
595d04451b | ||
|
|
6681cfbca6 | ||
|
|
14074894f9 | ||
|
|
3fcbf58873 | ||
|
|
1c2a12eb05 | ||
|
|
ec5bda6ddf | ||
|
|
5875015c11 | ||
|
|
21e9303502 | ||
|
|
94d4a219ee |
@@ -33,6 +33,7 @@ Contributors:
|
||||
Ryan Gray, Jeff Blake (https://gitlab.com/berkley4) : battery format
|
||||
aaaz (https://gitlab.com/aaaz) : clock fixes
|
||||
heisenbug (https://gitlab.com/heisenbugh) : taskbar button tinting with icon color
|
||||
Fabian Carlström : taskbar sort order by app name
|
||||
|
||||
Translations:
|
||||
Bosnian:
|
||||
|
||||
@@ -152,6 +152,7 @@ set( SOURCES src/config.c
|
||||
src/separator/separator.c
|
||||
src/tint2rc.c
|
||||
src/util/area.c
|
||||
src/util/bt.c
|
||||
src/util/common.c
|
||||
src/util/fps_distribution.c
|
||||
src/util/strnatcmp.c
|
||||
|
||||
@@ -1,3 +1,20 @@
|
||||
2019-02-17 master
|
||||
- Fix spacing around icons in executor without text in vertical panels (issue #716)
|
||||
- Fix Bug: Clock Only Updates Every Minute With Format %s (issue #724)
|
||||
- Fix markup drawing when font shadow enabled (issue #709)
|
||||
- Blink battery when low (issue #723)
|
||||
|
||||
2018-08-05 16.6.1
|
||||
- Fix packaging regression for debian (issue #715)
|
||||
|
||||
2018-08-04 16.6
|
||||
- Fix regression in task icon brightness (issue #714)
|
||||
|
||||
2018-08-04 16.5
|
||||
- Add new build script
|
||||
- Add option to sort taskbar by application name
|
||||
- Fix regression in image loading
|
||||
|
||||
2018-05-03 16.4
|
||||
- Update AUTHORS
|
||||
- Fixes:
|
||||
@@ -1006,3 +1023,5 @@ released tint-0.2
|
||||
.
|
||||
.
|
||||
.
|
||||
.
|
||||
.
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
# Latest stable release: 16.4
|
||||
Changes: https://gitlab.com/o9000/tint2/blob/16.4/ChangeLog
|
||||
# Latest stable release: 16.6.1
|
||||
Changes: https://gitlab.com/o9000/tint2/blob/16.6.1/ChangeLog
|
||||
|
||||
Documentation: [doc/tint2.md](doc/tint2.md)
|
||||
|
||||
@@ -8,7 +8,7 @@ Compile it with (after you install the [dependencies](https://gitlab.com/o9000/t
|
||||
```
|
||||
git clone https://gitlab.com/o9000/tint2.git
|
||||
cd tint2
|
||||
git checkout 16.4
|
||||
git checkout 16.6.1
|
||||
mkdir build
|
||||
cd build
|
||||
cmake ..
|
||||
|
||||
+2
-1
@@ -466,6 +466,7 @@ panel_size = 94% 30
|
||||
<ul>
|
||||
<li><code>none</code> : No sorting. New tasks are simply appended at the end of the taskbar when they appear.</li>
|
||||
<li><code>title</code> : Sorts the tasks by title.</li>
|
||||
<li><code>application</code> : Sorts the tasks by application name. <em>(since 16.3)</em></li>
|
||||
<li><code>center</code> : Sorts the tasks by their window centers.</li>
|
||||
<li><code>mru</code> : Shows the most recently used tasks first. <em>(since 0.12.4)</em></li>
|
||||
<li><code>lru</code> : Shows the most recently used tasks last. <em>(since 0.12.4)</em></li>
|
||||
@@ -572,7 +573,7 @@ panel_size = 94% 30
|
||||
<ul>
|
||||
<li><p><code>battery_hide = never/integer (0 to 100)</code> : At what battery percentage the battery item is hidden.</p></li>
|
||||
<li><p><code>battery_low_status = integer</code>: At what battery percentage the low command is executed.</p></li>
|
||||
<li><p><code>battery_low_cmd = notify-send "battery low"</code> : Command to execute when the battery is low.</p></li>
|
||||
<li><p><code>battery_low_cmd = xmessage 'tint2: Battery low!'</code> : Command to execute when the battery is low.</p></li>
|
||||
<li><p><code>battery_full_cmd = notify-send "battery full"</code> : Command to execute when the battery is full.</p></li>
|
||||
<li><p><code>bat1_font = [FAMILY-LIST] [STYLE-OPTIONS] [SIZE]</code></p></li>
|
||||
<li><p><code>bat2_font = [FAMILY-LIST] [STYLE-OPTIONS] [SIZE]</code></p></li>
|
||||
|
||||
+2
-2
@@ -199,9 +199,9 @@ pre {
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<h1 id="latest-stable-release-16-4"><span class="md2man-title">Latest</span> <span class="md2man-section">stable</span> <span class="md2man-date">release:</span> <span class="md2man-source">16.4</span><a name="latest-stable-release-16-4" href="#latest-stable-release-16-4" class="md2man-permalink" title="permalink"></a></h1><p>Changes: <a href="https://gitlab.com/o9000/tint2/blob/16.4/ChangeLog">https://gitlab.com/o9000/tint2/blob/16.4/ChangeLog</a></p><p>Documentation: <a href="manual.html">manual.html</a></p><p>Compile it with (after you install the <a href="https://gitlab.com/o9000/tint2/wikis/Install#dependencies">dependencies</a>):</p><pre class="highlight plaintext"><code>git clone https://gitlab.com/o9000/tint2.git
|
||||
<h1 id="latest-stable-release-16-6-1"><span class="md2man-title">Latest</span> <span class="md2man-section">stable</span> <span class="md2man-date">release:</span> <span class="md2man-source">16.6.1</span><a name="latest-stable-release-16-6-1" href="#latest-stable-release-16-6-1" class="md2man-permalink" title="permalink"></a></h1><p>Changes: <a href="https://gitlab.com/o9000/tint2/blob/16.6.1/ChangeLog">https://gitlab.com/o9000/tint2/blob/16.6.1/ChangeLog</a></p><p>Documentation: <a href="manual.html">manual.html</a></p><p>Compile it with (after you install the <a href="https://gitlab.com/o9000/tint2/wikis/Install#dependencies">dependencies</a>):</p><pre class="highlight plaintext"><code>git clone https://gitlab.com/o9000/tint2.git
|
||||
cd tint2
|
||||
git checkout 16.4
|
||||
git checkout 16.6.1
|
||||
mkdir build
|
||||
cd build
|
||||
cmake ..
|
||||
|
||||
+4
-2
@@ -1,4 +1,4 @@
|
||||
.TH TINT2 1 "2018\-05\-03" 16.4
|
||||
.TH TINT2 1 "2018\-08\-05" 16.6.1
|
||||
.SH NAME
|
||||
.PP
|
||||
tint2 \- lightweight panel/taskbar
|
||||
@@ -480,6 +480,8 @@ You can switch between virtual desktops.
|
||||
.IP \(bu 2
|
||||
\fB\fCtitle\fR : Sorts the tasks by title.
|
||||
.IP \(bu 2
|
||||
\fB\fCapplication\fR : Sorts the tasks by application name. \fI(since 16.3)\fP
|
||||
.IP \(bu 2
|
||||
\fB\fCcenter\fR : Sorts the tasks by their window centers.
|
||||
.IP \(bu 2
|
||||
\fB\fCmru\fR : Shows the most recently used tasks first. \fI(since 0.12.4)\fP
|
||||
@@ -663,7 +665,7 @@ To hide the clock, comment \fB\fCtime1_format\fR and \fB\fCtime2_format\fR\&.
|
||||
.IP \(bu 2
|
||||
\fB\fCbattery_low_status = integer\fR: At what battery percentage the low command is executed.
|
||||
.IP \(bu 2
|
||||
\fB\fCbattery_low_cmd = notify\-send "battery low"\fR : Command to execute when the battery is low.
|
||||
\fB\fCbattery_low_cmd = xmessage 'tint2: Battery low!'\fR : Command to execute when the battery is low.
|
||||
.IP \(bu 2
|
||||
\fB\fCbattery_full_cmd = notify\-send "battery full"\fR : Command to execute when the battery is full.
|
||||
.IP \(bu 2
|
||||
|
||||
+3
-2
@@ -1,4 +1,4 @@
|
||||
# TINT2 1 "2018-05-03" 16.4
|
||||
# TINT2 1 "2018-08-05" 16.6.1
|
||||
|
||||
## NAME
|
||||
tint2 - lightweight panel/taskbar
|
||||
@@ -385,6 +385,7 @@ panel_size = 94% 30
|
||||
* `taskbar_sort_order = none/title/center` : Specifies the sort order of the tasks on the taskbar. *(since 0.12)*
|
||||
* `none` : No sorting. New tasks are simply appended at the end of the taskbar when they appear.
|
||||
* `title` : Sorts the tasks by title.
|
||||
* `application` : Sorts the tasks by application name. *(since 16.3)*
|
||||
* `center` : Sorts the tasks by their window centers.
|
||||
* `mru` : Shows the most recently used tasks first. *(since 0.12.4)*
|
||||
* `lru` : Shows the most recently used tasks last. *(since 0.12.4)*
|
||||
@@ -546,7 +547,7 @@ The action semantics:
|
||||
|
||||
* `battery_low_status = integer`: At what battery percentage the low command is executed.
|
||||
|
||||
* `battery_low_cmd = notify-send "battery low"` : Command to execute when the battery is low.
|
||||
* `battery_low_cmd = xmessage 'tint2: Battery low!'` : Command to execute when the battery is low.
|
||||
|
||||
* `battery_full_cmd = notify-send "battery full"` : Command to execute when the battery is full.
|
||||
|
||||
|
||||
+10
-15
@@ -76,19 +76,23 @@ def get_last_version():
|
||||
return tags[-1]
|
||||
|
||||
|
||||
def inc_version(v, feature=False):
|
||||
def inc_version(v, feature=False, tiny=False):
|
||||
if v.startswith("v0."):
|
||||
assert v == "v0.14.6"
|
||||
return "v15.0"
|
||||
# v4.11 -> v4.12 or v5.0
|
||||
# v4.11 -> v4.12 or v5.0 or v4.11.1
|
||||
parts = v.split(".")
|
||||
while len(parts) < 2:
|
||||
while len(parts) < 3:
|
||||
parts.append("0")
|
||||
assert len(parts) == 2
|
||||
assert len(parts) == 3
|
||||
if feature:
|
||||
del parts[-1]
|
||||
parts[-2] = "v" + str(int(parts[-2].replace("v", "")) + 1)
|
||||
parts[-1] = "0"
|
||||
elif tiny:
|
||||
parts[-1] = str(int(parts[-1]) + 1)
|
||||
else:
|
||||
del parts[-1]
|
||||
parts[-1] = str(int(parts[-1]) + 1)
|
||||
return ".".join([s for s in parts if s])
|
||||
|
||||
@@ -153,23 +157,14 @@ def update_log(path, version, date):
|
||||
if __name__ == '__main__':
|
||||
parser = argparse.ArgumentParser()
|
||||
parser.add_argument("--feature", action="store_true")
|
||||
parser.add_argument("--undo", action="store_true")
|
||||
parser.add_argument("--tiny", action="store_true")
|
||||
args = parser.parse_args()
|
||||
logging.basicConfig(format=ansi_lblue + "%(asctime)s %(pathname)s %(levelname)s" + ansi_reset + " %(message)s", level=logging.DEBUG)
|
||||
test_inc_version()
|
||||
# Read version from last tag and increment
|
||||
old_version = get_last_version()
|
||||
if args.undo:
|
||||
info("Revering last commit...")
|
||||
run("git tag -d %s" % old_version)
|
||||
run("git tag -d %s" % old_version.replace("v", ""))
|
||||
run("git reset --soft HEAD~")
|
||||
run("git reset")
|
||||
run("git stash")
|
||||
os.system("git log -1")
|
||||
sys.exit(0)
|
||||
info("Old version:", old_version)
|
||||
version = inc_version(old_version, args.feature)
|
||||
version = inc_version(old_version, args.feature, args.tiny)
|
||||
readable_version = version.replace("v", "")
|
||||
date = datetime.datetime.now().strftime("%Y-%m-%d")
|
||||
info("New version:", readable_version, version, date)
|
||||
|
||||
+544
@@ -0,0 +1,544 @@
|
||||
#!/usr/bin/env python2.7
|
||||
|
||||
from __future__ import print_function
|
||||
|
||||
import argparse
|
||||
import errno
|
||||
import os
|
||||
import shlex
|
||||
import subprocess
|
||||
import sys
|
||||
import tempfile
|
||||
|
||||
|
||||
def check_c_compiles(cmd, code):
|
||||
with tempfile.NamedTemporaryFile(suffix='.c') as f:
|
||||
f.write(code)
|
||||
f.flush()
|
||||
cmd += [f.name, '-o', '/dev/null']
|
||||
proc = subprocess.Popen(
|
||||
cmd,
|
||||
stdout=subprocess.PIPE,
|
||||
stderr=subprocess.PIPE)
|
||||
out, err = proc.communicate()
|
||||
ret = proc.returncode
|
||||
return ret == 0
|
||||
|
||||
|
||||
def makedirs(path):
|
||||
try:
|
||||
os.makedirs(path)
|
||||
except OSError as e:
|
||||
if e.errno == errno.EEXIST and os.path.isdir(path):
|
||||
pass
|
||||
else:
|
||||
raise
|
||||
|
||||
|
||||
def pkg_config(lib):
|
||||
def _pkgconfig(lib, query):
|
||||
lib = lib.replace('>=', ' >= ')
|
||||
cmd = 'pkg-config {} {}'.format(query, lib).split()
|
||||
popen = subprocess.Popen(cmd,
|
||||
stdout=subprocess.PIPE,
|
||||
stderr=subprocess.PIPE)
|
||||
out, err = popen.communicate()
|
||||
if popen.returncode != 0:
|
||||
print(cmd)
|
||||
print(err)
|
||||
raise ValueError("Could not find library: {}".format(lib))
|
||||
return out.split()
|
||||
|
||||
lf = _pkgconfig(lib, '--libs-only-l')
|
||||
cf = _pkgconfig(lib, '--cflags')
|
||||
ldf = _pkgconfig(lib, '--libs-only-L')
|
||||
return (lf, cf, ldf)
|
||||
|
||||
|
||||
def ninja_escape(s):
|
||||
s = s.replace(' ', '$ ')
|
||||
s = s.replace('\n', '$\n')
|
||||
return s
|
||||
|
||||
|
||||
def ninja_join(words):
|
||||
indent = ' '
|
||||
if len(words) > 1:
|
||||
prefix = '$\n' + indent
|
||||
else:
|
||||
prefix = ''
|
||||
spacer = ' $\n' + indent
|
||||
return prefix + spacer.join(ninja_escape(s) for s in words)
|
||||
|
||||
|
||||
def ninja_write_vars(f, **kwargs):
|
||||
for k, v in kwargs.items():
|
||||
f.write('{} = {}\n'.format(k, v))
|
||||
f.write('\n')
|
||||
|
||||
|
||||
def ninja_write_rule(f, name, **kwargs):
|
||||
f.write('rule {}\n'.format(name))
|
||||
for k, v in kwargs.items():
|
||||
f.write(' {} = {}\n'.format(k, v))
|
||||
f.write('\n')
|
||||
|
||||
|
||||
def remove_prefix(s, prefix):
|
||||
while prefix and s.startswith(prefix):
|
||||
s = s[len(prefix):]
|
||||
return s
|
||||
|
||||
|
||||
def install(prefix, suffix, dest_dir, dest_suffix=''):
|
||||
src_path = os.path.join(prefix, suffix) if suffix else prefix
|
||||
fname = os.path.basename(src_path)
|
||||
if dest_suffix:
|
||||
dest_dir = os.path.join(dest_dir, dest_suffix)
|
||||
dst_path = os.path.join(dest_dir, fname)
|
||||
return [(src_path, dst_path)]
|
||||
|
||||
|
||||
def install_dir(prefix, suffix, dest_dir, dest_suffix=''):
|
||||
result = []
|
||||
src_path = os.path.join(prefix, suffix)
|
||||
for root, dirs, files in os.walk(src_path):
|
||||
for f in files:
|
||||
fname = os.path.join(root, f)
|
||||
fsuffix = remove_prefix(remove_prefix(fname, prefix), '/')
|
||||
result += install(prefix, fsuffix, dest_dir, os.path.join(dest_suffix, os.path.dirname(fsuffix)))
|
||||
return result
|
||||
|
||||
|
||||
class Executable(object):
|
||||
def __init__(self, name):
|
||||
self.name = name
|
||||
self.cc = 'cc'
|
||||
self.cflags = []
|
||||
self.lflags = []
|
||||
self.libs = []
|
||||
self.sources = []
|
||||
self.pos = []
|
||||
self.install_exec = []
|
||||
self.install_data = []
|
||||
|
||||
def write_ninja(self, f):
|
||||
f.write('# Executable: {}\n'.format(self.name))
|
||||
ninja_write_vars(f, **{
|
||||
'cc_' + self.name: self.cc,
|
||||
'cflags_' + self.name: ninja_join(self.cflags),
|
||||
'lflags_' + self.name: ninja_join(self.lflags),
|
||||
'libs_' + self.name: ninja_join(self.libs)})
|
||||
ninja_write_rule(f, 'cc_' + self.name,
|
||||
command='$cc_{} -MMD -MT $out -MF $out.d $cflags_{} -c $in -o $out'.format(self.name, self.name),
|
||||
description='CC $out',
|
||||
depfile='$out.d',
|
||||
deps='gcc')
|
||||
ninja_write_rule(f, 'link_' + self.name,
|
||||
command='$cc_{} $lflags_{} -o $out $in $libs_{}'.format(self.name, self.name, self.name),
|
||||
description='LINK $out')
|
||||
ninja_write_rule(f, 'po2mo_' + self.name,
|
||||
command='msgfmt -o $out $in',
|
||||
description='GEN $out')
|
||||
f.write('# Compilation\n')
|
||||
for src in self.sources:
|
||||
f.write('build $build_dir/{}.{}.o: cc_{} $source_dir/{}\n'.format(src, self.name, self.name, src))
|
||||
f.write('# Translation\n')
|
||||
for po in self.pos:
|
||||
f.write('build $build_dir/{}.mo: po2mo_{} $source_dir/{}\n'.format(os.path.splitext(po)[0], self.name, po))
|
||||
f.write('# Linking\n')
|
||||
f.write('build $build_dir/{}: link_{} '.format(self.name, self.name))
|
||||
f.write(ninja_join(['$build_dir/{}.{}.o'.format(src, self.name) for src in self.sources]))
|
||||
f.write('\n')
|
||||
f.write('# Installation\n')
|
||||
for fin, fout in self.install_exec:
|
||||
f.write('build {}: install_exec {}\n'.format(fout, fin))
|
||||
for fin, fout in self.install_data:
|
||||
f.write('build {}: install_data {}\n'.format(fout, fin))
|
||||
f.write('build install_{}: phony '.format(self.name))
|
||||
f.write(ninja_join([fout for fin, fout in self.install_exec + self.install_data]))
|
||||
f.write('\n')
|
||||
f.write('# Uninstallation\n')
|
||||
for fin, fout in self.install_exec + self.install_data:
|
||||
f.write('build uninstall_{}: uninstall {}\n'.format(fout, fout))
|
||||
f.write('build uninstall_{}: phony '.format(self.name))
|
||||
f.write(ninja_join(['uninstall_{}'.format(fout) for fin, fout in self.install_exec + self.install_data]))
|
||||
f.write('\n')
|
||||
f.write('\n')
|
||||
|
||||
|
||||
|
||||
def generate_ninja(targets, source_dir, build_dir):
|
||||
ninja_file_name = os.path.join(build_dir, 'build.ninja')
|
||||
f = open(ninja_file_name, 'w')
|
||||
# `deps` was introduced in ninja 1.3.
|
||||
ninja_write_vars(f, ninja_required_version='1.3')
|
||||
ninja_write_vars(f, **{
|
||||
'source_dir':source_dir,
|
||||
'build_dir':build_dir
|
||||
})
|
||||
ninja_write_rule(f, 'install_exec',
|
||||
command='install -D -m0755 $in $out',
|
||||
description='INSTALL $out')
|
||||
ninja_write_rule(f, 'install_data',
|
||||
command='install -D -m0644 $in $out',
|
||||
description='INSTALL $out')
|
||||
ninja_write_rule(f, 'uninstall',
|
||||
command='rm -f $in',
|
||||
description='RM $in')
|
||||
for t in targets:
|
||||
t.write_ninja(f)
|
||||
f.write('# Targets\n')
|
||||
f.write('build all: phony ')
|
||||
f.write(ninja_join(['$build_dir/{}'.format(t.name) for t in targets]))
|
||||
f.write('\n')
|
||||
installs = sum([(t.install_exec + t.install_data) for t in targets], [])
|
||||
if installs:
|
||||
f.write('build install: phony ')
|
||||
f.write(ninja_join(['install_{}'.format(t.name) for t in targets]))
|
||||
f.write('\n')
|
||||
f.write('build uninstall: phony ')
|
||||
f.write(ninja_join(['uninstall_{}'.format(t.name) for t in targets]))
|
||||
f.write('\n')
|
||||
f.write('\n')
|
||||
f.write('default all\n')
|
||||
f.close()
|
||||
print('Wrote {}.'.format(ninja_file_name))
|
||||
print('Run `ninja -v -C {} all` to compile.'.format(build_dir))
|
||||
print('Run `ninja -v -C {} install` to install.'.format(build_dir))
|
||||
|
||||
|
||||
# Parse CLI options
|
||||
parser = argparse.ArgumentParser()
|
||||
parser.add_argument('--uevent', dest='uevent', action='store_true',
|
||||
help='Enable uevent support. Default: on under Linux')
|
||||
parser.add_argument('--debug', dest='debug', action='store_true',
|
||||
help='Enable debug build. Default: off')
|
||||
parser.add_argument('--asan', dest='asan', action='store_true',
|
||||
help='Enable AddressSanitizer. Default: off')
|
||||
parser.add_argument('--tracing', dest='tracing', action='store_true',
|
||||
help='Enable tracing. Default: off')
|
||||
parser.add_argument('--memory-tracing', dest='memory_tracing', action='store_true',
|
||||
help='Enable memory allocation tracing. Default: off')
|
||||
parser.add_argument('--prefix', help='Prefix for constructing the file installation paths. Default: /usr/local', default=None)
|
||||
parser.add_argument('--exec_prefix', help='Prefix for binary paths. Default: $prefix', default=None)
|
||||
parser.add_argument('--bindir', help='Path where executables must be installed. Default: $exec_prefix/bin', default=None)
|
||||
parser.add_argument('--sysconfdir', help='Path where config files must be installed. Default: /etc', default=None)
|
||||
parser.add_argument('--datarootdir', help='Path where data files must be installed. Default: $prefix/share', default=None)
|
||||
parser.add_argument('--localedir', help='Path where locale files must be installed. Default: $datarootdir/locale', default=None)
|
||||
parser.add_argument('--docdir', help='Path where documentation files must be installed. Default: $datarootdir/doc/tint2', default=None)
|
||||
parser.add_argument('--htmldir', help='Path where documentation files must be installed. Default: $docdir/html', default=None)
|
||||
parser.add_argument('--mandir', help='Path where man files must be installed. Default: $datarootdir/man', default=None)
|
||||
parser.add_argument('--home', dest='home', action='store_true',
|
||||
help='Install to $HOME (sets all paths accordingly). Default: off')
|
||||
args = parser.parse_args()
|
||||
|
||||
# Get relevant environment variables
|
||||
CC = os.environ.get('CC', 'cc')
|
||||
CFLAGS = shlex.split(os.environ.get('CFLAGS', ''))
|
||||
LFLAGS = shlex.split(os.environ.get('LDFLAGS', ''))
|
||||
LIBS = []
|
||||
|
||||
# Get paths
|
||||
source_dir = os.path.realpath(os.path.dirname(os.path.realpath(__file__)) + '/..')
|
||||
build_dir = os.path.join(os.getcwd(), 'build')
|
||||
if not args.home:
|
||||
prefix = args.prefix or '/usr/local'
|
||||
exec_prefix = args.exec_prefix or prefix
|
||||
bindir = args.bindir or os.path.join(exec_prefix, 'bin')
|
||||
datarootdir = args.datarootdir or os.path.join(prefix, 'share')
|
||||
sysconfdir = args.sysconfdir or '/etc'
|
||||
docdir = args.docdir or os.path.join(datarootdir, 'doc/tint2')
|
||||
htmldir = args.htmldir or os.path.join(docdir, 'html')
|
||||
localedir = args.localedir or os.path.join(datarootdir, 'locale')
|
||||
mandir = args.mandir or os.path.join(datarootdir, 'man')
|
||||
else:
|
||||
prefix = args.prefix or os.path.expanduser("~")
|
||||
exec_prefix = args.exec_prefix or prefix
|
||||
bindir = args.bindir or os.path.join(exec_prefix, 'bin')
|
||||
datarootdir = args.datarootdir or os.path.join(prefix, '.local/share')
|
||||
sysconfdir = args.sysconfdir or os.path.expanduser("~/.config/tint2")
|
||||
docdir = args.docdir or os.path.join(datarootdir, 'doc/tint2')
|
||||
htmldir = args.htmldir or os.path.join(docdir, 'html')
|
||||
localedir = args.localedir or os.path.join(datarootdir, 'locale')
|
||||
mandir = args.mandir or os.path.join(datarootdir, 'man')
|
||||
|
||||
|
||||
# Check if C11 is supported by the compiler, fall back to C99
|
||||
if check_c_compiles([CC],
|
||||
'''#define print(x) _Generic((x), default : print_unknown)(x)
|
||||
void print_unknown() {
|
||||
}
|
||||
int main () {
|
||||
print(0);
|
||||
}'''):
|
||||
CFLAGS += ['-std=c11', '-DHAS_GENERIC']
|
||||
else:
|
||||
print("No C11 support.")
|
||||
CFLAGS += ['-std=c99']
|
||||
|
||||
# Set mandatory flags
|
||||
CFLAGS += ['-g',
|
||||
'-Wall',
|
||||
'-Wextra',
|
||||
'-Wshadow',
|
||||
'-Wpointer-arith',
|
||||
'-Wno-deprecated',
|
||||
'-Wno-missing-field-initializers',
|
||||
'-Wno-unused-parameter',
|
||||
'-Wno-sign-compare',
|
||||
'-fno-strict-aliasing',
|
||||
'-pthread',
|
||||
'-D_BSD_SOURCE',
|
||||
'-D_DEFAULT_SOURCE',
|
||||
'-D_WITH_GETLINE',
|
||||
'-DENABLE_BATTERY']
|
||||
LFLAGS += ['-fno-strict-aliasing',
|
||||
'-pthread']
|
||||
LFLAGS += ['-L' + build_dir]
|
||||
|
||||
# Set platform dependent C flags
|
||||
if sys.platform.startswith('linux'):
|
||||
CFLAGS += ['-D_POSIX_C_SOURCE=200809L']
|
||||
|
||||
if sys.platform.startswith('freebsd') or sys.platform.startswith('openbsd') or sys.platform.startswith('dragonfly'):
|
||||
CFLAGS += ['-I/usr/local/include']
|
||||
LFLAGS += ['-L/usr/local/lib']
|
||||
|
||||
if sys.platform.startswith('linux') or args.uevent:
|
||||
CFLAGS += ['-DENABLE_UEVENT']
|
||||
|
||||
# Turn on color messages if supported
|
||||
if check_c_compiles([CC, '-fdiagnostics-color', '-c', '-x', 'c'], ''):
|
||||
CFLAGS += ['-fdiagnostics-color=always']
|
||||
|
||||
# Set project-specific include dirs
|
||||
CFLAGS += ['-I.']
|
||||
for inc in ['src',
|
||||
'src/battery',
|
||||
'src/clock',
|
||||
'src/systray',
|
||||
'src/taskbar',
|
||||
'src/launcher',
|
||||
'src/tooltip',
|
||||
'src/util',
|
||||
'src/execplugin',
|
||||
'src/button',
|
||||
'src/freespace',
|
||||
'src/separator']:
|
||||
CFLAGS += ['-I' + os.path.join(source_dir, inc)]
|
||||
|
||||
# Add mandatory library dependencies
|
||||
LIBS += ['-lm', '-lrt']
|
||||
|
||||
# Add mandatory libray dependencies detected with pkg-config
|
||||
for dep in ['x11',
|
||||
'xcomposite',
|
||||
'xdamage',
|
||||
'xinerama',
|
||||
'xext',
|
||||
'xrender',
|
||||
'xrandr>=1.3',
|
||||
'pangocairo',
|
||||
'pango',
|
||||
'cairo',
|
||||
'glib-2.0',
|
||||
'gobject-2.0',
|
||||
'imlib2>=1.4.2']:
|
||||
lib, cf, lf = pkg_config(dep)
|
||||
LIBS += lib
|
||||
CFLAGS += cf
|
||||
LFLAGS += lf
|
||||
|
||||
# Add optional library dependencies detected with pkg-config
|
||||
try:
|
||||
lib, cf, lf = pkg_config('librsvg-2.0>=2.14.0')
|
||||
LIBS += lib
|
||||
CFLAGS += cf + ['-DHAVE_RSVG']
|
||||
LFLAGS += lf
|
||||
except:
|
||||
print("No SVG support.")
|
||||
|
||||
try:
|
||||
lib, cf, lf = pkg_config('libstartup-notification-1.0>=0.12')
|
||||
LIBS += lib
|
||||
CFLAGS += cf + ['-DHAVE_SN', '-DSN_API_NOT_YET_FROZEN']
|
||||
LFLAGS += lf
|
||||
except:
|
||||
print("No startup notification support.")
|
||||
|
||||
# Add library dependencies detected with using successful compilation test
|
||||
bt = False
|
||||
if not bt:
|
||||
try:
|
||||
lib, cf, lf = pkg_config('libunwind')
|
||||
LIBS += lib
|
||||
CFLAGS += cf + ['-DHAS_LIBUNWIND']
|
||||
LFLAGS += lf
|
||||
bt = True
|
||||
print("Backtrace support via libunwind.")
|
||||
except:
|
||||
print("No backtrace support via libunwind.")
|
||||
if not bt:
|
||||
if check_c_compiles([CC, '-lbacktrace'], '''#include <backtrace.h>
|
||||
int main() {
|
||||
return 0;
|
||||
}'''):
|
||||
CFLAGS += ['-DHAS_BACKTRACE']
|
||||
LIBS += ['-lbacktrace']
|
||||
bt = True
|
||||
print("Backtrace support via libbacktrace.")
|
||||
else:
|
||||
print("No backtrace support via libbacktrace.")
|
||||
|
||||
# Add option-dependent flags
|
||||
if not args.debug:
|
||||
CFLAGS += ['-O2']
|
||||
|
||||
if args.asan:
|
||||
asan_flags = ['-fsanitize=address']
|
||||
CFLAGS += asan_flags
|
||||
LFLAGS += asan_flags
|
||||
|
||||
if args.tracing:
|
||||
CFLAGS += ['-finstrument-functions',
|
||||
'-finstrument-functions-exclude-file-list=tracing.c',
|
||||
'-finstrument-functions-exclude-function-list=get_time,gettime']
|
||||
|
||||
if args.asan or args.memory_tracing or args.tracing:
|
||||
trace_flags = ['-O0',
|
||||
'-fno-common',
|
||||
'-fno-omit-frame-pointer',
|
||||
'-rdynamic']
|
||||
CFLAGS += trace_flags
|
||||
LFLAGS += trace_flags + ['-fuse-ld=gold']
|
||||
|
||||
if args.memory_tracing:
|
||||
LIBS += ['-ldl']
|
||||
|
||||
# Define targets
|
||||
tint2 = Executable('tint2')
|
||||
tint2.cflags += CFLAGS
|
||||
tint2.lflags += LFLAGS
|
||||
tint2.libs += LIBS
|
||||
tint2.sources = ['src/config.c',
|
||||
'src/panel.c',
|
||||
'src/util/server.c',
|
||||
'src/main.c',
|
||||
'src/init.c',
|
||||
'src/util/signals.c',
|
||||
'src/util/tracing.c',
|
||||
'src/mouse_actions.c',
|
||||
'src/drag_and_drop.c',
|
||||
'src/default_icon.c',
|
||||
'src/clock/clock.c',
|
||||
'src/systray/systraybar.c',
|
||||
'src/launcher/launcher.c',
|
||||
'src/launcher/apps-common.c',
|
||||
'src/launcher/icon-theme-common.c',
|
||||
'src/launcher/xsettings-client.c',
|
||||
'src/launcher/xsettings-common.c',
|
||||
'src/taskbar/task.c',
|
||||
'src/taskbar/taskbar.c',
|
||||
'src/taskbar/taskbarname.c',
|
||||
'src/tooltip/tooltip.c',
|
||||
'src/execplugin/execplugin.c',
|
||||
'src/button/button.c',
|
||||
'src/freespace/freespace.c',
|
||||
'src/separator/separator.c',
|
||||
'src/tint2rc.c',
|
||||
'src/util/area.c',
|
||||
'src/util/bt.c',
|
||||
'src/util/common.c',
|
||||
'src/util/fps_distribution.c',
|
||||
'src/util/strnatcmp.c',
|
||||
'src/util/timer.c',
|
||||
'src/util/cache.c',
|
||||
'src/util/color.c',
|
||||
'src/util/strlcat.c',
|
||||
'src/util/print.c',
|
||||
'src/util/gradient.c',
|
||||
'src/util/test.c',
|
||||
'src/util/uevent.c',
|
||||
'src/util/window.c',
|
||||
'src/battery/battery.c']
|
||||
|
||||
# Battery implementation is platform-specific
|
||||
if sys.platform.startswith('linux'):
|
||||
tint2.sources += ['src/battery/linux.c']
|
||||
elif sys.platform.startswith('freebsd') or \
|
||||
sys.platform.startswith('dragonfly') or \
|
||||
sys.platform.startswith('gnukfreebsd'):
|
||||
tint2.sources += ['src/battery/freebsd.c']
|
||||
elif sys.platform.startswith('openbsd') or \
|
||||
sys.platform.startswith('netbsd'):
|
||||
tint2.sources += ['src/battery/openbsd.c']
|
||||
else:
|
||||
print("No battery support for platform:", sys.platform)
|
||||
tint2.sources += ['src/battery/dummy.c']
|
||||
|
||||
if args.memory_tracing:
|
||||
tint2.sources += ['src/util/mem.c']
|
||||
|
||||
tint2.install_exec = install(build_dir, 'tint2', bindir)
|
||||
tint2.install_data = (install(source_dir, 'tint2.svg', datarootdir, 'icons/hicolor/scalable/apps') +
|
||||
install(source_dir, 'tint2.desktop', datarootdir, 'applications') +
|
||||
install(source_dir, 'themes/tint2rc', sysconfdir, 'xdg/tint2') +
|
||||
install(source_dir, 'default_icon.png', datarootdir, 'tint2') +
|
||||
install(source_dir, 'AUTHORS', docdir) +
|
||||
install(source_dir, 'ChangeLog', docdir) +
|
||||
install(source_dir, 'README.md', docdir) +
|
||||
install(source_dir, 'doc/tint2.md', docdir) +
|
||||
install(source_dir, 'doc/manual.html', htmldir) +
|
||||
install(source_dir, 'doc/readme.html', htmldir) +
|
||||
install_dir(source_dir, 'doc/images', htmldir) +
|
||||
install(source_dir, 'doc/tint2.1', mandir, 'man1'))
|
||||
|
||||
tint2conf = Executable('tint2conf')
|
||||
tint2conf.cflags += CFLAGS
|
||||
tint2conf.lflags += LFLAGS
|
||||
tint2conf.libs += LIBS
|
||||
|
||||
for dep in ['gthread-2.0',
|
||||
'gtk+-x11-2.0']:
|
||||
lib, cf, lf = pkg_config(dep)
|
||||
tint2conf.libs += lib
|
||||
tint2conf.cflags += cf
|
||||
tint2conf.lflags += lf
|
||||
|
||||
tint2conf.cflags += ['-DTINT2CONF',
|
||||
'-DINSTALL_PREFIX=\\"{}\\"'.format(prefix),
|
||||
'-DLOCALEDIR=\\"{}\\"'.format(localedir),
|
||||
'-DGETTEXT_PACKAGE=\\"tint2conf\\"',
|
||||
'-DHAVE_VERSION_H',
|
||||
'-Wno-shadow']
|
||||
|
||||
tint2conf.sources = ['src/util/bt.c',
|
||||
'src/util/common.c',
|
||||
'src/util/strnatcmp.c',
|
||||
'src/util/cache.c',
|
||||
'src/util/timer.c',
|
||||
'src/util/test.c',
|
||||
'src/util/print.c',
|
||||
'src/util/signals.c',
|
||||
'src/config.c',
|
||||
'src/util/server.c',
|
||||
'src/util/strlcat.c',
|
||||
'src/launcher/apps-common.c',
|
||||
'src/launcher/icon-theme-common.c',
|
||||
'src/tint2conf/md4.c',
|
||||
'src/tint2conf/main.c',
|
||||
'src/tint2conf/properties.c',
|
||||
'src/tint2conf/properties_rw.c',
|
||||
'src/tint2conf/theme_view.c',
|
||||
'src/tint2conf/background_gui.c',
|
||||
'src/tint2conf/gradient_gui.c']
|
||||
tint2conf.pos = [os.path.join('src/tint2conf/po', f) for f in os.listdir('src/tint2conf/po') if f.endswith('.po')]
|
||||
tint2conf.install_exec = install(build_dir, 'tint2conf', bindir)
|
||||
tint2conf.install_data = (install(source_dir, 'src/tint2conf/tint2conf.svg', datarootdir, 'icons/hicolor/scalable/apps') +
|
||||
install(source_dir, 'src/tint2conf/tint2conf.desktop', datarootdir, 'applications') +
|
||||
install(source_dir, 'src/tint2conf/tint2conf.xml', datarootdir, 'mime/packages'))
|
||||
|
||||
makedirs(build_dir)
|
||||
assert 0 == os.system('cd {}; {}/get_version.sh'.format(build_dir, source_dir))
|
||||
generate_ninja([tint2, tint2conf], source_dir, build_dir)
|
||||
@@ -289,7 +289,7 @@ def get_gentoo_versions():
|
||||
|
||||
def get_void_versions():
|
||||
print >> sys.stderr, "Void ..."
|
||||
template = http_download_txt("https://raw.githubusercontent.com/voidlinux/void-packages/master/srcpkgs/tint2/template")
|
||||
template = http_download_txt("https://raw.githubusercontent.com/void-linux/void-packages/master/srcpkgs/tint2/template")
|
||||
versions = []
|
||||
version = None
|
||||
maintainer = None
|
||||
|
||||
+63
-1
@@ -42,6 +42,7 @@ gboolean battery_enabled;
|
||||
gboolean battery_tooltip_enabled;
|
||||
int percentage_hide;
|
||||
static Timer battery_timer;
|
||||
static Timer battery_blink_timer;
|
||||
|
||||
#define BATTERY_BUF_SIZE 256
|
||||
static char buf_bat_line1[BATTERY_BUF_SIZE];
|
||||
@@ -60,6 +61,9 @@ char *battery_rclick_command;
|
||||
char *battery_uwheel_command;
|
||||
char *battery_dwheel_command;
|
||||
gboolean battery_found;
|
||||
gboolean battery_warn;
|
||||
gboolean battery_warn_red;
|
||||
Background *battery_warn_bg;
|
||||
|
||||
char *battery_sys_prefix = (char *)"";
|
||||
|
||||
@@ -77,6 +81,10 @@ void default_battery()
|
||||
battery_low_cmd_sent = FALSE;
|
||||
battery_full_cmd_sent = FALSE;
|
||||
INIT_TIMER(battery_timer);
|
||||
INIT_TIMER(battery_blink_timer);
|
||||
battery_warn = FALSE;
|
||||
battery_warn_red = FALSE;
|
||||
battery_warn_bg = NULL;
|
||||
bat1_has_font = FALSE;
|
||||
bat1_font_desc = NULL;
|
||||
bat1_format = NULL;
|
||||
@@ -128,7 +136,9 @@ void cleanup_battery()
|
||||
free(ac_disconnected_cmd);
|
||||
ac_disconnected_cmd = NULL;
|
||||
destroy_timer(&battery_timer);
|
||||
destroy_timer(&battery_blink_timer);
|
||||
battery_found = FALSE;
|
||||
free_and_null(battery_warn_bg);
|
||||
|
||||
battery_os_free();
|
||||
}
|
||||
@@ -310,6 +320,40 @@ void battery_default_font_changed()
|
||||
schedule_panel_redraw();
|
||||
}
|
||||
|
||||
void blink_battery(void *arg)
|
||||
{
|
||||
if (!battery_enabled)
|
||||
return;
|
||||
battery_warn_red = battery_warn ? !battery_warn_red : FALSE;
|
||||
if (battery_warn_red && !battery_warn_bg) {
|
||||
battery_warn_bg = calloc(1, sizeof(*battery_warn_bg));
|
||||
*battery_warn_bg = *panel_config.battery.area.bg;
|
||||
battery_warn_bg->fill_color.alpha = 1.0;
|
||||
battery_warn_bg->border.color.alpha = 1.0;
|
||||
}
|
||||
if (battery_warn_red) {
|
||||
battery_warn_bg->fill_color.rgb[0] = 0.6;
|
||||
battery_warn_bg->fill_color.rgb[1] = 0.1;
|
||||
battery_warn_bg->fill_color.rgb[2] = 0.1;
|
||||
battery_warn_bg->border.color.rgb[0] = 0.5;
|
||||
battery_warn_bg->border.color.rgb[1] = 0.0;
|
||||
battery_warn_bg->border.color.rgb[2] = 0.0;
|
||||
} else {
|
||||
battery_warn_bg->fill_color.rgb[0] = 0.9;
|
||||
battery_warn_bg->fill_color.rgb[1] = 0.7;
|
||||
battery_warn_bg->fill_color.rgb[2] = 0.1;
|
||||
battery_warn_bg->border.color.rgb[0] = 0.7;
|
||||
battery_warn_bg->border.color.rgb[1] = 0.5;
|
||||
battery_warn_bg->border.color.rgb[2] = 0.1;
|
||||
}
|
||||
for (int i = 0; i < num_panels; i++) {
|
||||
if (panels[i].battery.area.on_screen) {
|
||||
panels[i].battery.area.bg = battery_warn_bg;
|
||||
schedule_redraw(&panels[i].battery.area);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void update_battery_tick(void *arg)
|
||||
{
|
||||
if (!battery_enabled)
|
||||
@@ -320,6 +364,7 @@ void update_battery_tick(void *arg)
|
||||
gboolean old_ac_connected = battery_state.ac_connected;
|
||||
int16_t old_hours = battery_state.time.hours;
|
||||
int8_t old_minutes = battery_state.time.minutes;
|
||||
gboolean old_warn = battery_warn;
|
||||
|
||||
if (!battery_found) {
|
||||
init_battery();
|
||||
@@ -357,6 +402,20 @@ void update_battery_tick(void *arg)
|
||||
battery_full_cmd_sent = FALSE;
|
||||
}
|
||||
|
||||
if (!battery_blink_timer.enabled_) {
|
||||
if (battery_state.percentage < battery_low_status &&
|
||||
battery_state.state == BATTERY_DISCHARGING) {
|
||||
change_timer(&battery_blink_timer, true, 10, 1000, blink_battery, 0);
|
||||
battery_warn = TRUE;
|
||||
}
|
||||
} else {
|
||||
if (battery_state.percentage > battery_low_status ||
|
||||
battery_state.state != BATTERY_DISCHARGING) {
|
||||
stop_timer(&battery_blink_timer);
|
||||
battery_warn = FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < num_panels; i++) {
|
||||
// Show/hide if needed
|
||||
if (!battery_found) {
|
||||
@@ -370,8 +429,11 @@ void update_battery_tick(void *arg)
|
||||
// Redraw if needed
|
||||
if (panels[i].battery.area.on_screen) {
|
||||
if (old_found != battery_found || old_percentage != battery_state.percentage ||
|
||||
old_hours != battery_state.time.hours || old_minutes != battery_state.time.minutes) {
|
||||
old_hours != battery_state.time.hours || old_minutes != battery_state.time.minutes ||
|
||||
old_warn != battery_warn) {
|
||||
panels[i].battery.area.resize_needed = TRUE;
|
||||
if (!battery_warn)
|
||||
panels[i].battery.area.bg = panel_config.battery.area.bg;
|
||||
schedule_panel_redraw();
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -478,7 +478,7 @@ void draw_button(void *obj, cairo_t *c)
|
||||
button->frontend->textx,
|
||||
button->frontend->texty,
|
||||
&button->backend->font_color,
|
||||
panel_config.font_shadow);
|
||||
panel_config.font_shadow ? layout : NULL);
|
||||
|
||||
g_object_unref(layout);
|
||||
g_object_unref(context);
|
||||
|
||||
+7
-1
@@ -173,7 +173,13 @@ gboolean time_format_needs_sec_ticks(char *time_format)
|
||||
{
|
||||
if (!time_format)
|
||||
return FALSE;
|
||||
if (strchr(time_format, 'S') || strchr(time_format, 'T') || strchr(time_format, 'r'))
|
||||
if (strchr(time_format, 'c') ||
|
||||
strchr(time_format, 'r') ||
|
||||
strchr(time_format, 's') ||
|
||||
strchr(time_format, 'S') ||
|
||||
strchr(time_format, 'T') ||
|
||||
strchr(time_format, 'X') ||
|
||||
strchr(time_format, '+'))
|
||||
return TRUE;
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
@@ -1016,6 +1016,8 @@ void add_entry(char *key, char *value)
|
||||
taskbar_sort_method = TASKBAR_SORT_CENTER;
|
||||
} else if (strcmp(value, "title") == 0) {
|
||||
taskbar_sort_method = TASKBAR_SORT_TITLE;
|
||||
} else if (strcmp(value, "application") == 0) {
|
||||
taskbar_sort_method = TASKBAR_SORT_APPLICATION;
|
||||
} else if (strcmp(value, "lru") == 0) {
|
||||
taskbar_sort_method = TASKBAR_SORT_LRU;
|
||||
} else if (strcmp(value, "mru") == 0) {
|
||||
|
||||
+45
-11
@@ -373,7 +373,11 @@ void execp_compute_icon_text_geometry(Execp *execp,
|
||||
*new_size = *txt_height + 2 * *vert_padding + top_bottom_border_width(area);
|
||||
*new_size = MAX(*new_size, *icon_h + 2 * *vert_padding + top_bottom_border_width(area));
|
||||
} else {
|
||||
if (strlen(execp->backend->text)) {
|
||||
*new_size = *icon_h + *interior_padding + *txt_height + 2 * *vert_padding + top_bottom_border_width(area);
|
||||
} else {
|
||||
*new_size = *icon_h + 2 * *vert_padding + top_bottom_border_width(area);
|
||||
}
|
||||
}
|
||||
if (*new_size != area->height) {
|
||||
*resized = TRUE;
|
||||
@@ -441,10 +445,17 @@ gboolean resize_execp(void *obj)
|
||||
execp->frontend->texty = (execp->area.height - txt_height) / 2;
|
||||
execp->frontend->textx = execp->frontend->iconx + icon_w + interior_padding;
|
||||
} else {
|
||||
if (strlen(execp->backend->text)) {
|
||||
execp->frontend->icony = (execp->area.height - icon_h - interior_padding - txt_height) / 2;
|
||||
execp->frontend->iconx = (execp->area.width - icon_w) / 2;
|
||||
execp->frontend->texty = execp->frontend->icony + icon_h + interior_padding;
|
||||
execp->frontend->textx = (execp->area.width - txt_width) / 2;
|
||||
} else {
|
||||
execp->frontend->icony = (execp->area.height - icon_h) / 2;
|
||||
execp->frontend->iconx = (execp->area.width - icon_w) / 2;
|
||||
execp->frontend->texty = execp->frontend->icony + icon_h + interior_padding;
|
||||
execp->frontend->textx = (execp->area.width - txt_width) / 2;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
execp->frontend->texty = (execp->area.height - txt_height) / 2;
|
||||
@@ -458,10 +469,17 @@ gboolean resize_execp(void *obj)
|
||||
execp->frontend->texty = (execp->area.height - txt_height) / 2;
|
||||
execp->frontend->textx = execp->frontend->iconx + icon_w + interior_padding;
|
||||
} else {
|
||||
if (strlen(execp->backend->text)) {
|
||||
execp->frontend->icony = (execp->area.height - icon_h - interior_padding - txt_height) / 2;
|
||||
execp->frontend->iconx = left_border_width(&execp->area) + horiz_padding;
|
||||
execp->frontend->texty = execp->frontend->icony + icon_h + interior_padding;
|
||||
execp->frontend->textx = execp->frontend->iconx;
|
||||
} else {
|
||||
execp->frontend->icony = (execp->area.height - icon_h) / 2;
|
||||
execp->frontend->iconx = left_border_width(&execp->area) + horiz_padding;
|
||||
execp->frontend->texty = execp->frontend->icony + icon_h + interior_padding;
|
||||
execp->frontend->textx = execp->frontend->iconx;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
execp->frontend->texty = (execp->area.height - txt_height) / 2;
|
||||
@@ -473,6 +491,18 @@ gboolean resize_execp(void *obj)
|
||||
return resized;
|
||||
}
|
||||
|
||||
PangoLayout *create_execp_text_layout(Execp *execp, PangoContext *context)
|
||||
{
|
||||
PangoLayout *layout = pango_layout_new(context);
|
||||
pango_layout_set_font_description(layout, execp->backend->font_desc);
|
||||
pango_layout_set_width(layout, (execp->frontend->textw + TINT2_PANGO_SLACK) * PANGO_SCALE);
|
||||
pango_layout_set_height(layout, (execp->frontend->texth + TINT2_PANGO_SLACK) * PANGO_SCALE);
|
||||
pango_layout_set_alignment(layout, execp->backend->centered ? PANGO_ALIGN_CENTER : PANGO_ALIGN_LEFT);
|
||||
pango_layout_set_wrap(layout, PANGO_WRAP_WORD_CHAR);
|
||||
pango_layout_set_ellipsize(layout, PANGO_ELLIPSIZE_NONE);
|
||||
return layout;
|
||||
}
|
||||
|
||||
void draw_execp(void *obj, cairo_t *c)
|
||||
{
|
||||
Execp *execp = (Execp *)obj;
|
||||
@@ -480,7 +510,8 @@ void draw_execp(void *obj, cairo_t *c)
|
||||
|
||||
PangoContext *context = pango_cairo_create_context(c);
|
||||
pango_cairo_context_set_resolution(context, 96 * panel->scale);
|
||||
PangoLayout *layout = pango_layout_new(context);
|
||||
PangoLayout *layout = create_execp_text_layout(execp, context);
|
||||
PangoLayout *shadow_layout = NULL;
|
||||
|
||||
if (execp->backend->has_icon && execp->backend->icon) {
|
||||
imlib_context_set_image(execp->backend->icon);
|
||||
@@ -489,16 +520,18 @@ void draw_execp(void *obj, cairo_t *c)
|
||||
}
|
||||
|
||||
// draw layout
|
||||
pango_layout_set_font_description(layout, execp->backend->font_desc);
|
||||
pango_layout_set_width(layout, (execp->frontend->textw + TINT2_PANGO_SLACK) * PANGO_SCALE);
|
||||
pango_layout_set_height(layout, (execp->frontend->texth + TINT2_PANGO_SLACK) * PANGO_SCALE);
|
||||
pango_layout_set_alignment(layout, execp->backend->centered ? PANGO_ALIGN_CENTER : PANGO_ALIGN_LEFT);
|
||||
pango_layout_set_wrap(layout, PANGO_WRAP_WORD_CHAR);
|
||||
pango_layout_set_ellipsize(layout, PANGO_ELLIPSIZE_NONE);
|
||||
if (!execp->backend->has_markup)
|
||||
if (!execp->backend->has_markup) {
|
||||
pango_layout_set_text(layout, execp->backend->text, strlen(execp->backend->text));
|
||||
else
|
||||
} else {
|
||||
pango_layout_set_markup(layout, execp->backend->text, strlen(execp->backend->text));
|
||||
if (panel_config.font_shadow) {
|
||||
shadow_layout = create_execp_text_layout(execp, context);
|
||||
if (!layout_set_markup_strip_colors(shadow_layout, execp->backend->text)) {
|
||||
g_object_unref(shadow_layout);
|
||||
shadow_layout = NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pango_cairo_update_layout(c, layout);
|
||||
draw_text(layout,
|
||||
@@ -506,7 +539,7 @@ void draw_execp(void *obj, cairo_t *c)
|
||||
execp->frontend->textx,
|
||||
execp->frontend->texty,
|
||||
&execp->backend->font_color,
|
||||
panel_config.font_shadow);
|
||||
shadow_layout);
|
||||
|
||||
g_object_unref(layout);
|
||||
g_object_unref(context);
|
||||
@@ -531,12 +564,13 @@ void execp_dump_geometry(void *obj, int indent)
|
||||
imlib_context_set_image(tmp);
|
||||
}
|
||||
fprintf(stderr,
|
||||
"tint2: %*sText: x = %d, y = %d, w = %d, align = %s, text = %s\n",
|
||||
"tint2: %*sText: x = %d, y = %d, w = %d, h = %d, align = %s, text = %s\n",
|
||||
indent,
|
||||
"",
|
||||
execp->frontend->textx,
|
||||
execp->frontend->texty,
|
||||
execp->frontend->textw,
|
||||
execp->frontend->texth,
|
||||
execp->backend->centered ? "center" : "left",
|
||||
execp->backend->text);
|
||||
}
|
||||
|
||||
+4
-3
@@ -209,8 +209,8 @@ void init_X11_pre_config()
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
server.x11_fd = ConnectionNumber(server.display);
|
||||
XSetErrorHandler((XErrorHandler)server_catch_error);
|
||||
XSetIOErrorHandler((XIOErrorHandler)x11_io_error);
|
||||
XSetErrorHandler(server_catch_error);
|
||||
XSetIOErrorHandler(x11_io_error);
|
||||
server_init_atoms();
|
||||
server.screen = DefaultScreen(server.display);
|
||||
server.root_win = RootWindow(server.display, server.screen);
|
||||
@@ -247,8 +247,9 @@ void init(int argc, char **argv)
|
||||
if (!config_read()) {
|
||||
fprintf(stderr, "tint2: Could not read config file.\n");
|
||||
print_usage();
|
||||
warnings_for_timers = false;
|
||||
cleanup();
|
||||
return;
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
init_post_config();
|
||||
|
||||
@@ -399,7 +399,12 @@ void launcher_icon_dump_geometry(void *obj, int indent)
|
||||
Imlib_Image scale_icon(Imlib_Image original, int icon_size)
|
||||
{
|
||||
Imlib_Image icon_scaled;
|
||||
if (original) {
|
||||
if (!icon_size) {
|
||||
icon_scaled = imlib_create_image(1, 1);
|
||||
imlib_context_set_image(icon_scaled);
|
||||
imlib_context_set_color(255, 255, 255, 255);
|
||||
imlib_image_fill_rectangle(0, 0, icon_size, icon_size);
|
||||
} else if (original) {
|
||||
imlib_context_set_image(original);
|
||||
icon_scaled = imlib_create_cropped_scaled_image(0,
|
||||
0,
|
||||
|
||||
+21
-3
@@ -107,6 +107,21 @@ Task *add_task(Window win)
|
||||
(int)win,
|
||||
task_template.title ? task_template.title : "null");
|
||||
|
||||
// get application name
|
||||
// use res_class property of WM_CLASS as res_name is easily overridable by user
|
||||
XClassHint *classhint = XAllocClassHint();
|
||||
if (classhint && XGetClassHint(server.display, win, classhint))
|
||||
task_template.application = strdup(classhint->res_class);
|
||||
else
|
||||
task_template.application = strdup("Untitled");
|
||||
if (classhint) {
|
||||
if (classhint->res_name)
|
||||
XFree(classhint->res_name);
|
||||
if (classhint->res_class)
|
||||
XFree(classhint->res_class);
|
||||
XFree(classhint);
|
||||
}
|
||||
|
||||
GPtrArray *task_buttons = g_ptr_array_new();
|
||||
for (int j = 0; j < panels[monitor].num_desktops; j++) {
|
||||
if (task_template.desktop != ALL_DESKTOPS && task_template.desktop != j)
|
||||
@@ -132,6 +147,7 @@ Task *add_task(Window win)
|
||||
task_instance->area.on_screen = always_show_all_desktop_tasks;
|
||||
}
|
||||
task_instance->title = task_template.title;
|
||||
task_instance->application = task_template.application;
|
||||
if (panels[monitor].g_task.tooltip_enabled) {
|
||||
task_instance->area._get_tooltip_text = task_get_tooltip;
|
||||
task_instance->area._get_tooltip_image = task_get_thumbnail;
|
||||
@@ -208,12 +224,14 @@ void remove_task(Task *task)
|
||||
|
||||
Window win = task->win;
|
||||
|
||||
// free title and icon just for the first task
|
||||
// free title, icon and application name just for the first task
|
||||
// even with task_on_all_desktop and with task_on_all_panel
|
||||
if (task->title)
|
||||
free(task->title);
|
||||
if (task->thumbnail)
|
||||
cairo_surface_destroy(task->thumbnail);
|
||||
if (task->application)
|
||||
free(task->application);
|
||||
task_remove_icon(task);
|
||||
|
||||
GPtrArray *task_buttons = g_hash_table_lookup(win_to_task, &win);
|
||||
@@ -381,7 +399,7 @@ void task_update_icon(Task *task)
|
||||
task->icon[k] = adjust_icon(orig_image,
|
||||
panel->g_task.alpha[k],
|
||||
panel->g_task.saturation[k],
|
||||
panel->g_task.brightness[k] != 0);
|
||||
panel->g_task.brightness[k]);
|
||||
if (panel_config.mouse_effects) {
|
||||
task->icon_hover[k] = adjust_icon(task->icon[k],
|
||||
panel_config.mouse_over_alpha,
|
||||
@@ -479,7 +497,7 @@ void draw_task(void *obj, cairo_t *c)
|
||||
task->_text_posy = (panel->g_task.area.height - task->_text_height) / 2.0;
|
||||
|
||||
Color *config_text = &panel->g_task.font[task->current_state];
|
||||
draw_text(layout, c, panel->g_task.text_posx, task->_text_posy, config_text, panel->font_shadow);
|
||||
draw_text(layout, c, panel->g_task.text_posx, task->_text_posy, config_text, panel->font_shadow ? layout : NULL);
|
||||
|
||||
g_object_unref(layout);
|
||||
g_object_unref(context);
|
||||
|
||||
@@ -68,6 +68,7 @@ typedef struct Task {
|
||||
Color icon_color_hover;
|
||||
Color icon_color_press;
|
||||
char *title;
|
||||
char *application;
|
||||
int urgent_tick;
|
||||
// These may not be up-to-date
|
||||
int win_x;
|
||||
|
||||
+12
-1
@@ -735,6 +735,14 @@ gint compare_task_titles(Task *a, Task *b, Taskbar *taskbar)
|
||||
return strnatcasecmp(a->title ? a->title : "", b->title ? b->title : "");
|
||||
}
|
||||
|
||||
gint compare_task_applications(Task *a, Task *b, Taskbar *taskbar)
|
||||
{
|
||||
int trivial = compare_tasks_trivial(a, b, taskbar);
|
||||
if (trivial != NONTRIVIAL)
|
||||
return trivial;
|
||||
return strnatcasecmp(a->application ? a->application : "", b->application ? b->application : "");
|
||||
}
|
||||
|
||||
gint compare_tasks(Task *a, Task *b, Taskbar *taskbar)
|
||||
{
|
||||
int trivial = compare_tasks_trivial(a, b, taskbar);
|
||||
@@ -746,6 +754,8 @@ gint compare_tasks(Task *a, Task *b, Taskbar *taskbar)
|
||||
return compare_task_centers(a, b, taskbar);
|
||||
} else if (taskbar_sort_method == TASKBAR_SORT_TITLE) {
|
||||
return compare_task_titles(a, b, taskbar);
|
||||
} else if (taskbar_sort_method == TASKBAR_SORT_APPLICATION) {
|
||||
return compare_task_applications(a, b, taskbar);
|
||||
} else if (taskbar_sort_method == TASKBAR_SORT_LRU) {
|
||||
return compare_timespecs(&a->last_activation_time, &b->last_activation_time);
|
||||
} else if (taskbar_sort_method == TASKBAR_SORT_MRU) {
|
||||
@@ -835,7 +845,8 @@ void taskbar_update_thumbnails(void *arg)
|
||||
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) ||
|
||||
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)
|
||||
|
||||
@@ -21,6 +21,7 @@ typedef enum TaskbarSortMethod {
|
||||
TASKBAR_NOSORT = 0,
|
||||
TASKBAR_SORT_CENTER,
|
||||
TASKBAR_SORT_TITLE,
|
||||
TASKBAR_SORT_APPLICATION,
|
||||
TASKBAR_SORT_LRU,
|
||||
TASKBAR_SORT_MRU,
|
||||
} TaskbarSortMethod;
|
||||
|
||||
@@ -215,7 +215,7 @@ void draw_taskbarname(void *obj, cairo_t *c)
|
||||
cairo_set_source_rgba(c, config_text->rgb[0], config_text->rgb[1], config_text->rgb[2], config_text->alpha);
|
||||
|
||||
pango_cairo_update_layout(c, layout);
|
||||
draw_text(layout, c, 0, taskbar_name->posy, config_text, ((Panel *)taskbar_name->area.panel)->font_shadow);
|
||||
draw_text(layout, c, 0, taskbar_name->posy, config_text, ((Panel *)taskbar_name->area.panel)->font_shadow ? layout : NULL);
|
||||
|
||||
g_object_unref(layout);
|
||||
g_object_unref(context);
|
||||
|
||||
@@ -20,6 +20,7 @@ include_directories( ../util
|
||||
${RSVG_INCLUDE_DIRS} )
|
||||
|
||||
set(SOURCES ../util/common.c
|
||||
../util/bt.c
|
||||
../util/strnatcmp.c
|
||||
../util/cache.c
|
||||
../util/timer.c
|
||||
|
||||
@@ -383,6 +383,11 @@ static void menuAbout()
|
||||
|
||||
// ====== Theme import/copy/delete ======
|
||||
|
||||
static void free_data(gpointer data, gpointer userdata)
|
||||
{
|
||||
g_free(data);
|
||||
}
|
||||
|
||||
// Shows open dialog and copies the selected files to ~ without overwrite.
|
||||
static void menuImportFile()
|
||||
{
|
||||
@@ -407,7 +412,7 @@ static void menuImportFile()
|
||||
gchar *newpath = import_no_overwrite(l->data);
|
||||
g_free(newpath);
|
||||
}
|
||||
g_slist_foreach(list, (GFunc)g_free, NULL);
|
||||
g_slist_foreach(list, free_data, NULL);
|
||||
g_slist_free(list);
|
||||
gtk_widget_destroy(dialog);
|
||||
}
|
||||
|
||||
+294
-287
File diff suppressed because it is too large
Load Diff
+294
-287
File diff suppressed because it is too large
Load Diff
+294
-287
File diff suppressed because it is too large
Load Diff
+294
-287
File diff suppressed because it is too large
Load Diff
+294
-287
File diff suppressed because it is too large
Load Diff
+294
-287
File diff suppressed because it is too large
Load Diff
+294
-287
File diff suppressed because it is too large
Load Diff
+289
-284
File diff suppressed because it is too large
Load Diff
@@ -2718,6 +2718,7 @@ void create_taskbar(GtkWidget *parent)
|
||||
col++;
|
||||
gtk_combo_box_append_text(GTK_COMBO_BOX(taskbar_sort_order), _("None"));
|
||||
gtk_combo_box_append_text(GTK_COMBO_BOX(taskbar_sort_order), _("By title"));
|
||||
gtk_combo_box_append_text(GTK_COMBO_BOX(taskbar_sort_order), _("By application"));
|
||||
gtk_combo_box_append_text(GTK_COMBO_BOX(taskbar_sort_order), _("By center"));
|
||||
gtk_combo_box_append_text(GTK_COMBO_BOX(taskbar_sort_order), _("Most recently used first"));
|
||||
gtk_combo_box_append_text(GTK_COMBO_BOX(taskbar_sort_order), _("Most recently used last"));
|
||||
@@ -2728,6 +2729,7 @@ void create_taskbar(GtkWidget *parent)
|
||||
"'None' means that new tasks are added to the end, and the user can also reorder task "
|
||||
"buttons by mouse dragging. \n"
|
||||
"'By title' means that tasks are sorted by their window titles. \n"
|
||||
"'By application' means that tasks are sorted by their application names. \n"
|
||||
"'By center' means that tasks are sorted geometrically by their window centers."),
|
||||
NULL);
|
||||
|
||||
|
||||
@@ -457,10 +457,12 @@ void config_write_taskbar(FILE *fp)
|
||||
} else if (gtk_combo_box_get_active(GTK_COMBO_BOX(taskbar_sort_order)) == 1) {
|
||||
fprintf(fp, "title");
|
||||
} else if (gtk_combo_box_get_active(GTK_COMBO_BOX(taskbar_sort_order)) == 2) {
|
||||
fprintf(fp, "center");
|
||||
fprintf(fp, "application");
|
||||
} else if (gtk_combo_box_get_active(GTK_COMBO_BOX(taskbar_sort_order)) == 3) {
|
||||
fprintf(fp, "mru");
|
||||
fprintf(fp, "center");
|
||||
} else if (gtk_combo_box_get_active(GTK_COMBO_BOX(taskbar_sort_order)) == 4) {
|
||||
fprintf(fp, "mru");
|
||||
} else if (gtk_combo_box_get_active(GTK_COMBO_BOX(taskbar_sort_order)) == 5) {
|
||||
fprintf(fp, "lru");
|
||||
} else {
|
||||
fprintf(fp, "none");
|
||||
@@ -1559,12 +1561,14 @@ void add_entry(char *key, char *value)
|
||||
gtk_combo_box_set_active(GTK_COMBO_BOX(taskbar_sort_order), 0);
|
||||
else if (strcmp(value, "title") == 0)
|
||||
gtk_combo_box_set_active(GTK_COMBO_BOX(taskbar_sort_order), 1);
|
||||
else if (strcmp(value, "center") == 0)
|
||||
else if (strcmp(value, "application") == 0)
|
||||
gtk_combo_box_set_active(GTK_COMBO_BOX(taskbar_sort_order), 2);
|
||||
else if (strcmp(value, "mru") == 0)
|
||||
else if (strcmp(value, "center") == 0)
|
||||
gtk_combo_box_set_active(GTK_COMBO_BOX(taskbar_sort_order), 3);
|
||||
else if (strcmp(value, "lru") == 0)
|
||||
else if (strcmp(value, "mru") == 0)
|
||||
gtk_combo_box_set_active(GTK_COMBO_BOX(taskbar_sort_order), 4);
|
||||
else if (strcmp(value, "lru") == 0)
|
||||
gtk_combo_box_set_active(GTK_COMBO_BOX(taskbar_sort_order), 5);
|
||||
else
|
||||
gtk_combo_box_set_active(GTK_COMBO_BOX(taskbar_sort_order), 0);
|
||||
} else if (strcmp(key, "task_align") == 0) {
|
||||
|
||||
+4
-4
@@ -478,14 +478,14 @@ void draw(Area *a)
|
||||
a->_clear(a);
|
||||
}
|
||||
|
||||
cairo_surface_t *cs = cairo_xlib_surface_create(server.display, a->pix, server.visual, a->width, a->height);
|
||||
cairo_surface_t *cs = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, a->width, a->height);
|
||||
cairo_t *c = cairo_create(cs);
|
||||
|
||||
draw_background(a, c);
|
||||
|
||||
if (a->_draw_foreground)
|
||||
a->_draw_foreground(a, c);
|
||||
|
||||
draw_cairo_surface_to_xpixmap(cs, a->pix);
|
||||
cairo_destroy(c);
|
||||
cairo_surface_destroy(cs);
|
||||
}
|
||||
@@ -1084,7 +1084,7 @@ void draw_text_area(Area *area,
|
||||
pango_layout_set_font_description(layout, line1_font_desc);
|
||||
pango_layout_set_text(layout, line1, strlen(line1));
|
||||
pango_cairo_update_layout(c, layout);
|
||||
draw_text(layout, c, (area->width - inner_w) / 2, line1_posy, color, ((Panel *)area->panel)->font_shadow);
|
||||
draw_text(layout, c, (area->width - inner_w) / 2, line1_posy, color, ((Panel *)area->panel)->font_shadow ? layout : NULL);
|
||||
}
|
||||
|
||||
if (line2 && line2[0]) {
|
||||
@@ -1092,7 +1092,7 @@ void draw_text_area(Area *area,
|
||||
pango_layout_set_indent(layout, 0);
|
||||
pango_layout_set_text(layout, line2, strlen(line2));
|
||||
pango_cairo_update_layout(c, layout);
|
||||
draw_text(layout, c, (area->width - inner_w) / 2, line2_posy, color, ((Panel *)area->panel)->font_shadow);
|
||||
draw_text(layout, c, (area->width - inner_w) / 2, line2_posy, color, ((Panel *)area->panel)->font_shadow ? layout : NULL);
|
||||
}
|
||||
|
||||
g_object_unref(layout);
|
||||
|
||||
+268
@@ -0,0 +1,268 @@
|
||||
/**************************************************************************
|
||||
*
|
||||
* Tint2 : backtrace
|
||||
*
|
||||
* Copyright (C) 2007 Pål Staurland (staura@gmail.com)
|
||||
* Modified (C) 2008 thierry lorthiois (lorthiois@bbsoft.fr) from Omega distribution
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License version 2
|
||||
* as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
**************************************************************************/
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include "bt.h"
|
||||
#include "bool.h"
|
||||
|
||||
#if defined(HAS_BACKTRACE) || defined(HAS_LIBUNWIND) || defined(HAS_EXECINFO)
|
||||
|
||||
static void bt_add_frame(struct backtrace *bt, const char *fname)
|
||||
{
|
||||
if (bt->frame_count >= BT_MAX_FRAMES)
|
||||
return;
|
||||
struct backtrace_frame *frame = &bt->frames[bt->frame_count];
|
||||
if (fname && *fname) {
|
||||
strncpy(frame->name, fname, BT_FRAME_SIZE);
|
||||
} else {
|
||||
strncpy(frame->name, "??", BT_FRAME_SIZE);
|
||||
}
|
||||
bt->frame_count++;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef HAS_BACKTRACE
|
||||
|
||||
#include <backtrace.h>
|
||||
|
||||
static const char *get_exe()
|
||||
{
|
||||
static char buf[256] = {0};
|
||||
ssize_t ret = readlink("/proc/self/exe", buf, sizeof(buf)-1);
|
||||
if (ret > 0)
|
||||
return buf;
|
||||
ret = readlink("/proc/curproc/file", buf, sizeof(buf)-1);
|
||||
if (ret > 0)
|
||||
return buf;
|
||||
return buf;
|
||||
}
|
||||
|
||||
static void bt_error_callback(void *data, const char *msg, int errnum)
|
||||
{
|
||||
}
|
||||
|
||||
static int bt_full_callback(void *data, uintptr_t pc,
|
||||
const char *filename, int lineno,
|
||||
const char *function)
|
||||
{
|
||||
struct backtrace *bt = (struct backtrace *)data;
|
||||
bt_add_frame(bt, function);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void get_backtrace(struct backtrace *bt, int skip)
|
||||
{
|
||||
static struct backtrace_state *state = NULL;
|
||||
|
||||
if (!state) {
|
||||
const char *exe = get_exe();
|
||||
if (exe)
|
||||
state = backtrace_create_state(exe, 1, bt_error_callback, NULL);
|
||||
}
|
||||
bzero(bt, sizeof(*bt));
|
||||
if (state) {
|
||||
backtrace_full(state, skip + 1, bt_full_callback, bt_error_callback, bt);
|
||||
}
|
||||
}
|
||||
|
||||
#elif HAS_LIBUNWIND
|
||||
|
||||
#define UNW_LOCAL_ONLY
|
||||
#include <libunwind.h>
|
||||
|
||||
struct bt_mapping {
|
||||
unw_word_t ip;
|
||||
char fname[BT_FRAME_SIZE];
|
||||
};
|
||||
|
||||
#define BT_BUCKET_SIZE 3
|
||||
struct bt_bucket {
|
||||
struct bt_mapping mappings[BT_BUCKET_SIZE];
|
||||
};
|
||||
|
||||
struct bt_cache {
|
||||
struct bt_bucket *buckets;
|
||||
size_t count;
|
||||
size_t size;
|
||||
};
|
||||
|
||||
static unsigned oat_hash(void *key, int len)
|
||||
{
|
||||
unsigned char *p = key;
|
||||
unsigned h = 0;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < len; i++)
|
||||
{
|
||||
h += p[i];
|
||||
h += (h << 10);
|
||||
h ^= (h >> 6);
|
||||
}
|
||||
|
||||
h += (h << 3);
|
||||
h ^= (h >> 11);
|
||||
h += (h << 15);
|
||||
|
||||
return h;
|
||||
}
|
||||
|
||||
static struct bt_bucket *cached_proc_name_bucket(struct bt_cache *cache, unw_word_t ip)
|
||||
{
|
||||
if (!cache->size)
|
||||
return NULL;
|
||||
unsigned h = oat_hash(&ip, sizeof(ip));
|
||||
return &cache->buckets[h % cache->size];
|
||||
}
|
||||
|
||||
static void bt_cache_init(struct bt_cache *cache)
|
||||
{
|
||||
if (!cache->size) {
|
||||
cache->size = 119;
|
||||
cache->buckets = calloc(cache->size, sizeof(*cache->buckets));
|
||||
if (!cache->buckets)
|
||||
cache->size = 0;
|
||||
cache->count = 0;
|
||||
}
|
||||
}
|
||||
|
||||
static void cached_proc_name_store(struct bt_cache *cache, unw_word_t ip, const char *fname);
|
||||
|
||||
static void bt_cache_rebalance(struct bt_cache *cache)
|
||||
{
|
||||
struct bt_cache bigger = {};
|
||||
bigger.size = cache->size * 2 + 1337;
|
||||
bigger.buckets = calloc(bigger.size, sizeof(*bigger.buckets));
|
||||
bigger.count = 0;
|
||||
for (size_t b = 0; b < cache->size; b++) {
|
||||
for (size_t i = 0; i < BT_BUCKET_SIZE; i++) {
|
||||
struct bt_mapping *map = &cache->buckets[b].mappings[i];
|
||||
if (map->ip) {
|
||||
cached_proc_name_store(&bigger, map->ip, map->fname);
|
||||
}
|
||||
}
|
||||
}
|
||||
free(cache->buckets);
|
||||
*cache = bigger;
|
||||
}
|
||||
|
||||
static void cached_proc_name_store(struct bt_cache *cache, unw_word_t ip, const char *fname)
|
||||
{
|
||||
bt_cache_init(cache);
|
||||
if (!cache->size)
|
||||
return;
|
||||
struct bt_bucket *bucket = cached_proc_name_bucket(cache, ip);
|
||||
bool stored = false;
|
||||
for (size_t i = 0; i < BT_BUCKET_SIZE; i++) {
|
||||
if (bucket->mappings[i].ip == ip)
|
||||
return;
|
||||
if (bucket->mappings[i].ip != 0)
|
||||
continue;
|
||||
bucket->mappings[i].ip = ip;
|
||||
strncpy(bucket->mappings[i].fname, fname, sizeof(bucket->mappings[i].fname));
|
||||
cache->count++;
|
||||
stored = true;
|
||||
break;
|
||||
}
|
||||
if (cache->count > cache->size / 4 || !stored) {
|
||||
bt_cache_rebalance(cache);
|
||||
fprintf(stderr, "tint2: proc_name cache: ratio %f, count %lu, size %lu, (%lu bytes)\n",
|
||||
cache->count / (double)cache->size,
|
||||
cache->count, (size_t)cache->size, cache->size * sizeof(*cache->buckets));
|
||||
}
|
||||
}
|
||||
|
||||
const char *cached_proc_name_get(struct bt_cache *cache, unw_word_t ip)
|
||||
{
|
||||
struct bt_bucket *bucket = cached_proc_name_bucket(cache, ip);
|
||||
if (!bucket)
|
||||
return NULL;
|
||||
for (size_t i = 0; i < BT_BUCKET_SIZE; i++) {
|
||||
if (bucket->mappings[i].ip != ip)
|
||||
continue;
|
||||
return bucket->mappings[i].fname;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void get_backtrace(struct backtrace *bt, int skip)
|
||||
{
|
||||
static struct bt_cache bt_cache = {};
|
||||
bzero(bt, sizeof(*bt));
|
||||
|
||||
unw_cursor_t cursor;
|
||||
unw_context_t context;
|
||||
unw_getcontext(&context);
|
||||
unw_init_local(&cursor, &context);
|
||||
|
||||
while (unw_step(&cursor) > 0) {
|
||||
if (skip > 0) {
|
||||
skip--;
|
||||
continue;
|
||||
}
|
||||
unw_word_t offset;
|
||||
char fname[BT_FRAME_SIZE] = {0};
|
||||
unw_word_t ip;
|
||||
unw_get_reg(&cursor, UNW_REG_IP, &ip);
|
||||
const char *fname_cached = cached_proc_name_get(&bt_cache, ip);
|
||||
if (fname_cached) {
|
||||
bt_add_frame(bt, fname_cached);
|
||||
} else {
|
||||
unw_get_proc_name(&cursor, fname, sizeof(fname), &offset);
|
||||
cached_proc_name_store(&bt_cache, ip, fname);
|
||||
bt_add_frame(bt, fname);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#elif HAS_EXECINFO
|
||||
|
||||
#include <execinfo.h>
|
||||
|
||||
void get_backtrace(struct backtrace *bt, int skip)
|
||||
{
|
||||
bzero(bt, sizeof(*bt));
|
||||
|
||||
void *array[BT_MAX_FRAMES];
|
||||
int size = backtrace(array, BT_MAX_FRAMES);
|
||||
char **strings = backtrace_symbols(array, size);
|
||||
|
||||
for (int i = 0; i < size; i++) {
|
||||
bt_add_frame(bt, strings[i]);
|
||||
}
|
||||
|
||||
free(strings);
|
||||
}
|
||||
|
||||
|
||||
#else
|
||||
|
||||
void get_backtrace(struct backtrace *bt, int skip)
|
||||
{
|
||||
bzero(bt, sizeof(*bt));
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,20 @@
|
||||
#ifndef BT_H
|
||||
#define BT_H
|
||||
|
||||
#include <sys/types.h>
|
||||
|
||||
#define BT_FRAME_SIZE 64
|
||||
#define BT_MAX_FRAMES 64
|
||||
|
||||
struct backtrace_frame {
|
||||
char name[BT_FRAME_SIZE];
|
||||
};
|
||||
|
||||
struct backtrace {
|
||||
struct backtrace_frame frames[BT_MAX_FRAMES];
|
||||
size_t frame_count;
|
||||
};
|
||||
|
||||
void get_backtrace(struct backtrace *bt, int skip);
|
||||
|
||||
#endif // BT_H
|
||||
+69
-46
@@ -48,18 +48,10 @@
|
||||
#include <librsvg/rsvg.h>
|
||||
#endif
|
||||
|
||||
#ifdef ENABLE_LIBUNWIND
|
||||
#define UNW_LOCAL_ONLY
|
||||
#include <libunwind.h>
|
||||
#else
|
||||
#ifdef ENABLE_EXECINFO
|
||||
#include <execinfo.h>
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#include "../panel.h"
|
||||
#include "timer.h"
|
||||
#include "signals.h"
|
||||
#include "bt.h"
|
||||
|
||||
void write_string(int fd, const char *s)
|
||||
{
|
||||
@@ -83,39 +75,13 @@ void log_string(int fd, const char *s)
|
||||
|
||||
void dump_backtrace(int log_fd)
|
||||
{
|
||||
#ifndef DISABLE_BACKTRACE
|
||||
struct backtrace bt;
|
||||
get_backtrace(&bt, 1);
|
||||
log_string(log_fd, "\n" YELLOW "Backtrace:" RESET "\n");
|
||||
|
||||
#ifdef ENABLE_LIBUNWIND
|
||||
unw_cursor_t cursor;
|
||||
unw_context_t context;
|
||||
unw_getcontext(&context);
|
||||
unw_init_local(&cursor, &context);
|
||||
|
||||
while (unw_step(&cursor) > 0) {
|
||||
unw_word_t offset;
|
||||
char fname[128];
|
||||
fname[0] = '\0';
|
||||
(void)unw_get_proc_name(&cursor, fname, sizeof(fname), &offset);
|
||||
log_string(log_fd, fname);
|
||||
for (size_t i = 0; i < bt.frame_count; i++) {
|
||||
log_string(log_fd, bt.frames[i].name);
|
||||
log_string(log_fd, "\n");
|
||||
}
|
||||
#else
|
||||
#ifdef ENABLE_EXECINFO
|
||||
#define MAX_TRACE_SIZE 128
|
||||
void *array[MAX_TRACE_SIZE];
|
||||
size_t size = backtrace(array, MAX_TRACE_SIZE);
|
||||
char **strings = backtrace_symbols(array, size);
|
||||
|
||||
for (size_t i = 0; i < size; i++) {
|
||||
log_string(log_fd, strings[i]);
|
||||
log_string(log_fd, "\n");
|
||||
}
|
||||
|
||||
free(strings);
|
||||
#endif // ENABLE_EXECINFO
|
||||
#endif // ENABLE_LIBUNWIND
|
||||
#endif // DISABLE_BACKTRACE
|
||||
}
|
||||
|
||||
// sleep() returns early when signals arrive. This function does not.
|
||||
@@ -396,8 +362,12 @@ pid_t tint_exec(const char *command,
|
||||
// Allow children to exist after parent destruction
|
||||
setsid();
|
||||
// Run the command
|
||||
if (dir)
|
||||
chdir(dir);
|
||||
if (dir) {
|
||||
int ret = chdir(dir);
|
||||
if (ret != 0) {
|
||||
fprintf(stderr, "tint2: failed to chdir to %s\n", dir);
|
||||
}
|
||||
}
|
||||
close_all_fds();
|
||||
reset_signals();
|
||||
if (terminal) {
|
||||
@@ -758,9 +728,37 @@ void render_image(Drawable d, int x, int y)
|
||||
XFreePixmap(server.display, pixmap);
|
||||
}
|
||||
|
||||
void draw_text(PangoLayout *layout, cairo_t *c, int posx, int posy, Color *color, int font_shadow)
|
||||
gboolean is_color_attribute(PangoAttribute *attr, gpointer user_data)
|
||||
{
|
||||
return attr->klass->type == PANGO_ATTR_FOREGROUND ||
|
||||
attr->klass->type == PANGO_ATTR_BACKGROUND ||
|
||||
attr->klass->type == PANGO_ATTR_UNDERLINE_COLOR ||
|
||||
attr->klass->type == PANGO_ATTR_STRIKETHROUGH_COLOR ||
|
||||
attr->klass->type == PANGO_ATTR_FOREGROUND_ALPHA ||
|
||||
attr->klass->type == PANGO_ATTR_BACKGROUND_ALPHA;
|
||||
}
|
||||
|
||||
gboolean layout_set_markup_strip_colors(PangoLayout *layout, const char *markup)
|
||||
{
|
||||
PangoAttrList *attrs = NULL;
|
||||
char *text = NULL;
|
||||
GError *error = NULL;
|
||||
if (!pango_parse_markup(markup, -1, 0, &attrs, &text, NULL, &error)) {
|
||||
g_error_free(error);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
pango_layout_set_text(layout, text, -1);
|
||||
g_free(text);
|
||||
|
||||
pango_attr_list_filter(attrs, is_color_attribute, NULL);
|
||||
pango_layout_set_attributes(layout, attrs);
|
||||
pango_attr_list_unref(attrs);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
void draw_shadow(cairo_t *c, int posx, int posy, PangoLayout *shadow_layout)
|
||||
{
|
||||
if (font_shadow) {
|
||||
const int shadow_size = 3;
|
||||
const double shadow_edge_alpha = 0.0;
|
||||
int i, j;
|
||||
@@ -773,12 +771,17 @@ void draw_text(PangoLayout *layout, cairo_t *c, int posx, int posy, Color *color
|
||||
1.0 -
|
||||
(1.0 - shadow_edge_alpha) *
|
||||
sqrt((i * i + j * j) / (double)(shadow_size * shadow_size)));
|
||||
pango_cairo_update_layout(c, layout);
|
||||
pango_cairo_update_layout(c, shadow_layout);
|
||||
cairo_move_to(c, posx + i, posy + j);
|
||||
pango_cairo_show_layout(c, layout);
|
||||
pango_cairo_show_layout(c, shadow_layout);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void draw_text(PangoLayout *layout, cairo_t *c, int posx, int posy, Color *color, PangoLayout *shadow_layout)
|
||||
{
|
||||
if (shadow_layout)
|
||||
draw_shadow(c, posx, posy, shadow_layout);
|
||||
cairo_set_source_rgba(c, color->rgb[0], color->rgb[1], color->rgb[2], color->alpha);
|
||||
pango_cairo_update_layout(c, layout);
|
||||
cairo_move_to(c, posx, posy);
|
||||
@@ -788,11 +791,15 @@ void draw_text(PangoLayout *layout, cairo_t *c, int posx, int posy, Color *color
|
||||
Imlib_Image load_image(const char *path, int cached)
|
||||
{
|
||||
Imlib_Image image;
|
||||
static unsigned long counter = 0;
|
||||
if (debug_icons)
|
||||
fprintf(stderr, "tint2: loading icon %s\n", path);
|
||||
#ifdef HAVE_RSVG
|
||||
image = imlib_load_image(path);
|
||||
if (!image && g_str_has_suffix(path, ".svg")) {
|
||||
char tmp_filename[128];
|
||||
snprintf(tmp_filename, sizeof(tmp_filename), "/tmp/tint2-%d.png", (int)getpid());
|
||||
snprintf(tmp_filename, sizeof(tmp_filename), "/tmp/tint2-%d-%lu.png", (int)getpid(), counter);
|
||||
counter++;
|
||||
int fd = open(tmp_filename, O_CREAT | O_EXCL, 0600);
|
||||
if (fd >= 0) {
|
||||
// We fork here because librsvg allocates memory like crazy
|
||||
@@ -1222,3 +1229,19 @@ void dump_image_data(const char *file_name, const char *name)
|
||||
|
||||
imlib_free_image();
|
||||
}
|
||||
|
||||
void draw_cairo_surface_to_xpixmap(cairo_surface_t *cs, Pixmap pix)
|
||||
{
|
||||
int w = cairo_image_surface_get_width(cs);
|
||||
int h = cairo_image_surface_get_height(cs);
|
||||
cairo_surface_t *cs_xlib = cairo_xlib_surface_create(server.display, pix, server.visual, w, h);
|
||||
cairo_xlib_surface_set_size(cs_xlib, w, h);
|
||||
cairo_t *c_xlib = cairo_create(cs_xlib);
|
||||
|
||||
cairo_set_source_surface(c_xlib, cs, 0, 0);
|
||||
cairo_paint(c_xlib);
|
||||
cairo_surface_flush(cs_xlib);
|
||||
|
||||
cairo_destroy(c_xlib);
|
||||
cairo_surface_destroy(cs_xlib);
|
||||
}
|
||||
|
||||
+4
-1
@@ -126,7 +126,8 @@ void get_text_size2(const PangoFontDescription *font,
|
||||
gboolean markup,
|
||||
double scale);
|
||||
|
||||
void draw_text(PangoLayout *layout, cairo_t *c, int posx, int posy, Color *color, int font_shadow);
|
||||
gboolean layout_set_markup_strip_colors(PangoLayout *layout, const char *markup);
|
||||
void draw_text(PangoLayout *layout, cairo_t *c, int posx, int posy, Color *color, PangoLayout *shadow_layout);
|
||||
|
||||
// Draws a rounded rectangle
|
||||
void draw_rect(cairo_t *c, double x, double y, double w, double h, double r);
|
||||
@@ -153,6 +154,8 @@ void get_image_mean_color(const Imlib_Image image, Color *mean_color);
|
||||
|
||||
void dump_image_data(const char *file_name, const char *name);
|
||||
|
||||
void draw_cairo_surface_to_xpixmap(cairo_surface_t *cs, Pixmap pix);
|
||||
|
||||
#define free_and_null(p) \
|
||||
{ \
|
||||
free(p); \
|
||||
|
||||
+331
@@ -0,0 +1,331 @@
|
||||
#define _GNU_SOURCE
|
||||
#include <dlfcn.h>
|
||||
#include <endian.h>
|
||||
#include <fcntl.h>
|
||||
#include <pthread.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/time.h>
|
||||
#include <sys/types.h>
|
||||
#include <time.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/mman.h>
|
||||
#include <limits.h>
|
||||
#include <signal.h>
|
||||
|
||||
#include "bt.h"
|
||||
#include "bool.h"
|
||||
|
||||
#define UNUSED(x) ((void)x)
|
||||
|
||||
void ERR(const char *s)
|
||||
{
|
||||
if (!s) {
|
||||
ERR("(null)");
|
||||
return;
|
||||
}
|
||||
ssize_t ret = write(STDERR_FILENO, s, strlen(s));
|
||||
UNUSED(ret);
|
||||
ret = fsync(STDERR_FILENO);
|
||||
UNUSED(ret);
|
||||
}
|
||||
|
||||
void utoa(unsigned long n, char *s)
|
||||
{
|
||||
if (n == 0) {
|
||||
*s++ = '0';
|
||||
*s = 0;
|
||||
return;
|
||||
}
|
||||
char buffer[128] = {0};
|
||||
char *digit;
|
||||
for (digit = buffer; n; digit++, n/=10) {
|
||||
*digit = '0' + (n % 10);
|
||||
}
|
||||
digit--;
|
||||
while (digit >= buffer) {
|
||||
*s++ = *digit--;
|
||||
}
|
||||
*s = 0;
|
||||
}
|
||||
|
||||
unsigned long uabs(long n)
|
||||
{
|
||||
if (n == LONG_MIN)
|
||||
return ((unsigned long)LONG_MAX) + 1;
|
||||
return (unsigned long)labs(n);
|
||||
}
|
||||
|
||||
void itoa(long n, char *s)
|
||||
{
|
||||
if (n < 0) {
|
||||
*s++ = '-';
|
||||
}
|
||||
utoa(uabs(n), s);
|
||||
}
|
||||
|
||||
__attribute__((noreturn))
|
||||
static void crash(char *file, int line, char *msg)
|
||||
{
|
||||
ERR(file);
|
||||
ERR(":");
|
||||
char buf[256];
|
||||
itoa(line, buf);
|
||||
ERR(buf);
|
||||
ERR(" ");
|
||||
ERR(msg);
|
||||
_exit(1);
|
||||
}
|
||||
|
||||
#define ASSERT_OK(call) if (0 != (call)) crash(__FILE__, __LINE__, #call)
|
||||
#define ASSERT_FD(fd) if ((fd) == -1) crash(__FILE__, __LINE__, "bad file descriptor")
|
||||
#define ASSERT_PID(pid) if ((pid) == -1) crash(__FILE__, __LINE__, "bad PID")
|
||||
#define ASSERT(b) if (!(b)) crash(__FILE__, __LINE__, "assert failed")
|
||||
|
||||
void *return_null(void *x, void *y)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
void load_func_or_crash(void **result, const char *name)
|
||||
{
|
||||
static int inside_loader = 0;
|
||||
if (inside_loader) {
|
||||
*result = (void*)return_null;
|
||||
return;
|
||||
} else if (*result == (void*)return_null) {
|
||||
*result = 0;
|
||||
}
|
||||
if (*result)
|
||||
return;
|
||||
dlerror();
|
||||
inside_loader++;
|
||||
*result = dlsym(RTLD_NEXT, name);
|
||||
inside_loader--;
|
||||
char *err = dlerror();
|
||||
if (err) {
|
||||
ERR("Failed to load ");
|
||||
ERR(name);
|
||||
ERR(" error: ");
|
||||
ERR(err);
|
||||
ERR("\n");
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
static int fd = -1;
|
||||
static u_int64_t tstart = 0;
|
||||
static bool stop_alloc_log = false;
|
||||
|
||||
static void write_char(char c)
|
||||
{
|
||||
static char buffer[4096] = {0};
|
||||
static size_t count = 0;
|
||||
if (c) {
|
||||
buffer[count++] = c;
|
||||
}
|
||||
if (!count)
|
||||
return;
|
||||
if (c == '\n' || c == 0 || count >= sizeof(buffer)) {
|
||||
ssize_t ret = write(fd, buffer, count);
|
||||
ASSERT(ret > 0);
|
||||
count = 0;
|
||||
}
|
||||
}
|
||||
|
||||
static void write_string(const char *s)
|
||||
{
|
||||
for (; *s; s++)
|
||||
write_char(*s);
|
||||
}
|
||||
|
||||
static void write_word(const char *s)
|
||||
{
|
||||
write_string(s);
|
||||
write_string(" ");
|
||||
}
|
||||
|
||||
static char hex(u_int8_t value)
|
||||
{
|
||||
if (value < 10)
|
||||
return '0' + (char)value;
|
||||
return 'a' + (char)(value - 10);
|
||||
}
|
||||
|
||||
static void write_hex(u_int64_t v)
|
||||
{
|
||||
write_string("0x");
|
||||
if (!v) {
|
||||
write_string("0 ");
|
||||
return;
|
||||
}
|
||||
v = htobe64(v);
|
||||
int leading_zero = 1;
|
||||
while (v) {
|
||||
u_int8_t byte = v & 0xff;
|
||||
if (byte)
|
||||
leading_zero = 0;
|
||||
if (!leading_zero) {
|
||||
write_char(hex(byte >> 4));
|
||||
write_char(hex(byte & 0xf));
|
||||
}
|
||||
v = v >> 8;
|
||||
}
|
||||
write_string(" ");
|
||||
}
|
||||
|
||||
static void write_backtrace(int skip)
|
||||
{
|
||||
struct backtrace bt;
|
||||
get_backtrace(&bt, skip + 1);
|
||||
for (size_t i = 0; i < bt.frame_count; i++) {
|
||||
write_word(bt.frames[i].name);
|
||||
}
|
||||
}
|
||||
|
||||
static void log_alloc_finish(void);
|
||||
|
||||
static u_int64_t current_time_ms()
|
||||
{
|
||||
struct timespec t = {0, 0};
|
||||
clock_gettime(CLOCK_MONOTONIC, &t);
|
||||
u_int64_t result = t.tv_sec * 1000 + t.tv_nsec / 1000 / 1000;
|
||||
return result;
|
||||
}
|
||||
|
||||
static void log_alloc_init(u_int64_t t)
|
||||
{
|
||||
if (stop_alloc_log)
|
||||
return;
|
||||
if (fd == -1) {
|
||||
int pfd[2] = {-1, -1};
|
||||
ASSERT_OK(pipe(pfd));
|
||||
pid_t child = fork();
|
||||
ASSERT_PID(child);
|
||||
if (child == 0) {
|
||||
// child
|
||||
close(pfd[1]);
|
||||
ASSERT_FD(dup2(pfd[0], STDIN_FILENO));
|
||||
ASSERT_OK(close(pfd[0]));
|
||||
int out = open("mem.log.gz", O_APPEND | O_CLOEXEC | O_CREAT | O_WRONLY | O_TRUNC, 0600);
|
||||
ASSERT_FD(out);
|
||||
ASSERT_FD(dup2(out, STDOUT_FILENO));
|
||||
sigset_t mask;
|
||||
sigfillset(&mask);
|
||||
sigprocmask(SIG_SETMASK, &mask, NULL);
|
||||
ASSERT_OK(execlp("gzip", "gzip", "-c", NULL));
|
||||
_exit(1);
|
||||
} else {
|
||||
// parent
|
||||
close(pfd[0]);
|
||||
fd = pfd[1];
|
||||
}
|
||||
atexit(log_alloc_finish);
|
||||
tstart = t;
|
||||
write_string("# function time_ms result ptr size count backtrace\n");
|
||||
}
|
||||
}
|
||||
|
||||
static void log_alloc_locked(const char *func_name, void *result, void *ptr, size_t size, size_t count)
|
||||
{
|
||||
if (stop_alloc_log)
|
||||
return;
|
||||
u_int64_t t = current_time_ms();
|
||||
if (fd == -1)
|
||||
log_alloc_init(t);
|
||||
if (fd == -1)
|
||||
return;
|
||||
if (func_name) {
|
||||
write_word(func_name);
|
||||
write_hex((u_int64_t)(t - tstart));
|
||||
write_hex((u_int64_t)result);
|
||||
write_hex((u_int64_t)ptr);
|
||||
write_hex((u_int64_t)size);
|
||||
write_hex((u_int64_t)count);
|
||||
write_backtrace(2);
|
||||
write_string("\n");
|
||||
} else {
|
||||
write_string("# done\n");
|
||||
close(fd);
|
||||
fd = -1;
|
||||
stop_alloc_log = true;
|
||||
}
|
||||
}
|
||||
|
||||
static int pid = -1;
|
||||
static void log_alloc(const char *func_name, void *result, void *ptr, size_t size, size_t count)
|
||||
{
|
||||
static pthread_mutex_t mutex_global = PTHREAD_MUTEX_INITIALIZER;
|
||||
static pthread_mutex_t mutex_recursive;
|
||||
static pthread_mutex_t mutex_nonrecursive = PTHREAD_MUTEX_INITIALIZER;
|
||||
static bool mutexes_initialized = false;
|
||||
|
||||
pthread_mutex_lock(&mutex_global);
|
||||
{
|
||||
if (!mutexes_initialized) {
|
||||
pthread_mutexattr_t attr;
|
||||
pthread_mutexattr_init(&attr);
|
||||
pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE);
|
||||
pthread_mutex_init(&mutex_recursive, &attr);
|
||||
mutexes_initialized = true;
|
||||
pid = getpid();
|
||||
}
|
||||
}
|
||||
pthread_mutex_unlock(&mutex_global);
|
||||
|
||||
// Do not log from forked processes.
|
||||
if (pid != getpid())
|
||||
return;
|
||||
|
||||
pthread_mutex_lock(&mutex_recursive);
|
||||
int ret = pthread_mutex_trylock(&mutex_nonrecursive);
|
||||
if (ret == 0) {
|
||||
log_alloc_locked(func_name, result, ptr, size, count);
|
||||
pthread_mutex_unlock(&mutex_nonrecursive);
|
||||
}
|
||||
pthread_mutex_unlock(&mutex_recursive);
|
||||
}
|
||||
|
||||
static void log_alloc_finish()
|
||||
{
|
||||
log_alloc(0, 0, 0, 0, 0);
|
||||
}
|
||||
|
||||
void *malloc(size_t size)
|
||||
{
|
||||
static void *(*original)(size_t size) = 0;
|
||||
load_func_or_crash((void *)&original, __FUNCTION__);
|
||||
void *result = original(size);
|
||||
log_alloc(__FUNCTION__, result, 0, size, 0);
|
||||
return result;
|
||||
}
|
||||
|
||||
void *realloc(void *ptr, size_t size)
|
||||
{
|
||||
static void *(*original)(void *p, size_t size) = 0;
|
||||
load_func_or_crash((void *)&original, __FUNCTION__);
|
||||
void *result = original(ptr, size);
|
||||
log_alloc(__FUNCTION__, result, ptr, size, 0);
|
||||
return result;
|
||||
}
|
||||
|
||||
void *calloc(size_t nmemb, size_t size)
|
||||
{
|
||||
static void *(*original)(size_t nmemb, size_t size) = 0;
|
||||
load_func_or_crash((void *)&original, __FUNCTION__);
|
||||
void *result = original(nmemb, size);
|
||||
log_alloc(__FUNCTION__, result, 0, size, nmemb);
|
||||
return result;
|
||||
}
|
||||
|
||||
void free(void *ptr)
|
||||
{
|
||||
static void *(*original)(void *p) = 0;
|
||||
load_func_or_crash((void *)&original, __FUNCTION__);
|
||||
if (!original) {
|
||||
return;
|
||||
}
|
||||
original(ptr);
|
||||
log_alloc(__FUNCTION__, 0, ptr, 0, 0);
|
||||
}
|
||||
+4
-2
@@ -36,8 +36,9 @@
|
||||
|
||||
Server server;
|
||||
|
||||
void server_catch_error(Display *d, XErrorEvent *ev)
|
||||
int server_catch_error(Display *d, XErrorEvent *ev)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
void server_init_atoms()
|
||||
@@ -676,9 +677,10 @@ void handle_crash(const char *reason)
|
||||
#endif
|
||||
}
|
||||
|
||||
void x11_io_error(Display *display)
|
||||
int x11_io_error(Display *display)
|
||||
{
|
||||
handle_crash("X11 I/O error");
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef HAVE_SN
|
||||
|
||||
+2
-2
@@ -164,12 +164,12 @@ void send_event32(Window win, Atom at, long data1, long data2, long data3);
|
||||
int get_property32(Window win, Atom at, Atom type);
|
||||
void *server_get_property(Window win, Atom at, Atom type, int *num_results);
|
||||
Atom server_get_atom(char *atom_name);
|
||||
void server_catch_error(Display *d, XErrorEvent *ev);
|
||||
int server_catch_error(Display *d, XErrorEvent *ev);
|
||||
void server_init_atoms();
|
||||
void server_init_visual();
|
||||
void server_init_xdamage();
|
||||
|
||||
void x11_io_error(Display *display);
|
||||
int x11_io_error(Display *display);
|
||||
void handle_crash(const char *reason);
|
||||
|
||||
// detect root background
|
||||
|
||||
+2
-1
@@ -25,6 +25,7 @@
|
||||
#include "timer.h"
|
||||
#include "test.h"
|
||||
|
||||
bool warnings_for_timers = true;
|
||||
bool debug_timers = false;
|
||||
#define MOCK_ORIGIN 1000000
|
||||
|
||||
@@ -59,7 +60,7 @@ void init_timer(Timer *timer, const char *name)
|
||||
|
||||
void destroy_timer(Timer *timer)
|
||||
{
|
||||
if (!g_list_find(timers, timer)) {
|
||||
if (warnings_for_timers && !g_list_find(timers, timer)) {
|
||||
fprintf(stderr, RED "tint2: Attempt to destroy nonexisting timer: %s" RESET "\n", timer->name_);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -23,6 +23,7 @@
|
||||
#include <sys/time.h>
|
||||
#include "bool.h"
|
||||
|
||||
extern bool warnings_for_timers;
|
||||
extern bool debug_timers;
|
||||
|
||||
typedef void TimerCallback(void *arg);
|
||||
|
||||
+110
-115
@@ -21,6 +21,7 @@
|
||||
#include <X11/Xlib.h>
|
||||
#include <X11/Xutil.h>
|
||||
#include <X11/Xatom.h>
|
||||
#include <X11/extensions/Xrender.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
@@ -383,28 +384,42 @@ void smooth_thumbnail(cairo_surface_t *image_surface)
|
||||
}
|
||||
}
|
||||
|
||||
// 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
|
||||
// This is measured to be slightly faster than XGetPixel.
|
||||
static u_int32_t GetPixel(XImage *ximg, unsigned x, unsigned y)
|
||||
{
|
||||
if (x >= ximg->width || y >= ximg->height) {
|
||||
fprintf(stderr, RED "GetPixel read overflow: %u %u %d %d\n",
|
||||
x, y, ximg->width, ximg->height);
|
||||
return 0;
|
||||
}
|
||||
return ((u_int32_t *)&(ximg->data[y * ximg->bytes_per_line]))[x];
|
||||
}
|
||||
|
||||
// TODO: the xrender method only works well if there is a compositor running
|
||||
cairo_surface_t *get_window_thumbnail_ximage(Window win, size_t size, gboolean use_shm)
|
||||
{
|
||||
fprintf(stderr, GREEN "tint2: win=%u: getting thumbnail of size %u" RESET "\n", win, size);
|
||||
cairo_surface_t *result = NULL;
|
||||
XWindowAttributes wa;
|
||||
if (!XGetWindowAttributes(server.display, win, &wa) || wa.width <= 0 || wa.height <= 0 ||
|
||||
wa.map_state != IsViewable)
|
||||
wa.map_state != IsViewable) {
|
||||
fprintf(stderr, YELLOW "tint2: win=%u: no win attributes" RESET "\n", win);
|
||||
goto err0;
|
||||
}
|
||||
|
||||
if (window_is_iconified(win))
|
||||
if (window_is_iconified(win)) {
|
||||
fprintf(stderr, YELLOW "tint2: win=%u: no thumbnail for minimized window" RESET "\n", win);
|
||||
goto err0;
|
||||
}
|
||||
|
||||
size_t w, h;
|
||||
w = (size_t)wa.width;
|
||||
h = (size_t)wa.height;
|
||||
w = (size_t)wa.width + wa.x;
|
||||
h = (size_t)wa.height + wa.y;
|
||||
size_t tw, th, fw;
|
||||
size_t ox, oy;
|
||||
tw = size;
|
||||
th = h * tw / w;
|
||||
double scale = th / (double)h;
|
||||
if (th > tw * 0.618) {
|
||||
th = (size_t)(tw * 0.618);
|
||||
fw = w * th / h;
|
||||
@@ -414,20 +429,69 @@ cairo_surface_t *get_window_thumbnail_ximage(Window win, size_t size, gboolean u
|
||||
fw = tw;
|
||||
ox = oy = 0;
|
||||
}
|
||||
if (!w || !h || !tw || !th || !fw) {
|
||||
fprintf(stderr, YELLOW "tint2: win=%u: no thumbnail for size 0x0" RESET "\n", win);
|
||||
goto err0;
|
||||
}
|
||||
|
||||
XShmSegmentInfo shminfo;
|
||||
XImage *ximg;
|
||||
if (use_shm)
|
||||
ximg = XShmCreateImage(server.display,
|
||||
wa.visual,
|
||||
(unsigned)wa.depth,
|
||||
ZPixmap,
|
||||
NULL,
|
||||
&shminfo,
|
||||
(unsigned)w,
|
||||
(unsigned)h);
|
||||
else
|
||||
ximg = XGetImage(server.display, win, 0, 0, (unsigned)w, (unsigned)h, AllPlanes, ZPixmap);
|
||||
XImage *ximg = NULL;
|
||||
|
||||
XGetWindowAttributes(server.display, win, &wa);
|
||||
if (wa.map_state != IsViewable) {
|
||||
fprintf(stderr, YELLOW "tint2: win=%u: no thumbnail for non-viewable window" RESET "\n", win);
|
||||
goto err4;
|
||||
}
|
||||
|
||||
Pixmap pix = XCreatePixmap(server.display, win, tw, th, wa.depth);
|
||||
if (!pix) {
|
||||
fprintf(stderr, YELLOW "tint2: win=%u: no pixmap" RESET "\n", win);
|
||||
goto err4;
|
||||
}
|
||||
XRenderPictureAttributes attrs = {
|
||||
.repeat = RepeatNone
|
||||
};
|
||||
XRenderPictFormat *fmt = XRenderFindVisualFormat(server.display, wa.visual);
|
||||
if (!fmt) {
|
||||
fprintf(stderr, YELLOW "tint2: win=%u: no XRender format" RESET "\n", win);
|
||||
goto err4;
|
||||
}
|
||||
Picture src_pic = XRenderCreatePicture(server.display, win, fmt, CPRepeat, &attrs);
|
||||
if (!src_pic) {
|
||||
fprintf(stderr, YELLOW "tint2: win=%u: no src picture" RESET "\n", win);
|
||||
goto err4;
|
||||
}
|
||||
Picture dst_pic = XRenderCreatePicture(server.display, pix, fmt, CPRepeat, &attrs);
|
||||
if (!dst_pic) {
|
||||
fprintf(stderr, YELLOW "tint2: win=%u: no src picture" RESET "\n", win);
|
||||
goto err4;
|
||||
}
|
||||
fprintf(stderr, GREEN "tint2: win=%u: transformation %u %u %f xy %d %d" RESET "\n", win, ox, oy, scale,
|
||||
wa.x, wa.y);
|
||||
XTransform transform = {
|
||||
{
|
||||
{
|
||||
XDoubleToFixed(1./scale),
|
||||
XDoubleToFixed(0),
|
||||
XDoubleToFixed(ox-wa.x/scale)
|
||||
},
|
||||
{
|
||||
XDoubleToFixed(0),
|
||||
XDoubleToFixed(1./scale),
|
||||
XDoubleToFixed(oy-wa.y/scale)
|
||||
},
|
||||
{
|
||||
XDoubleToFixed(0),
|
||||
XDoubleToFixed(0),
|
||||
XDoubleToFixed(1)
|
||||
}
|
||||
}
|
||||
};
|
||||
XRenderSetPictureTransform(server.display, src_pic, &transform);
|
||||
XRenderSetPictureFilter(server.display, src_pic, FilterBilinear, NULL, 0);
|
||||
XRenderComposite(server.display, PictOpSrc, src_pic, None, dst_pic, 0, 0, 0, 0, 0, 0, w, h);
|
||||
XSync(server.display, False);
|
||||
|
||||
ximg = XGetImage(server.display, pix, 0, 0, (unsigned)tw, (unsigned)th, AllPlanes, ZPixmap);
|
||||
if (!ximg) {
|
||||
fprintf(stderr, RED "tint2: !ximg" RESET "\n");
|
||||
goto err0;
|
||||
@@ -436,87 +500,32 @@ cairo_surface_t *get_window_thumbnail_ximage(Window win, size_t size, gboolean u
|
||||
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;
|
||||
|
||||
fprintf(stderr, GREEN "tint2: creating image surface %ux%u" RESET "\n", tw, th);
|
||||
result = cairo_image_surface_create(CAIRO_FORMAT_RGB24, (int)tw, (int)th);
|
||||
u_int32_t *data = (u_int32_t *)cairo_image_surface_get_data(result);
|
||||
memset(data, 0, tw * th);
|
||||
|
||||
// Fixed-point precision
|
||||
const size_t prec = 1 << 16;
|
||||
const size_t xstep = w * prec / fw;
|
||||
const size_t ystep = h * prec / th;
|
||||
|
||||
const size_t offset_y1 = 0 * ystep / 8;
|
||||
const size_t offset_x1 = 3 * xstep / 8;
|
||||
|
||||
const size_t offset_y2 = 1 * ystep / 8;
|
||||
const size_t offset_x2 = 6 * xstep / 8;
|
||||
|
||||
const size_t offset_y3 = 4 * ystep / 8;
|
||||
const size_t offset_x3 = 2 * xstep / 8;
|
||||
|
||||
const size_t offset_y4 = 4 * ystep / 8;
|
||||
const size_t offset_x4 = 4 * xstep / 8;
|
||||
|
||||
const size_t offset_y5 = 4 * ystep / 8;
|
||||
const size_t offset_x5 = 7 * xstep / 8;
|
||||
|
||||
const size_t offset_y6 = 6 * ystep / 8;
|
||||
const size_t offset_x6 = 1 * xstep / 8;
|
||||
|
||||
const size_t offset_y7 = 7 * ystep / 8;
|
||||
const size_t offset_x7 = 6 * xstep / 8;
|
||||
|
||||
const u_int32_t rmask = (u_int32_t)ximg->red_mask;
|
||||
const u_int32_t gmask = (u_int32_t)ximg->green_mask;
|
||||
const u_int32_t bmask = (u_int32_t)ximg->blue_mask;
|
||||
for (size_t yt = 0, y = 0; yt < th; yt++, y += ystep) {
|
||||
for (size_t xt = 0, x = 0; xt < fw; xt++, x += xstep) {
|
||||
size_t j = yt * tw + ox + xt;
|
||||
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);
|
||||
for (size_t y = 0; y < th; y++) {
|
||||
for (size_t x = 0; x < tw; x++) {
|
||||
u_int32_t c = (u_int32_t)XGetPixel(ximg, x, y);
|
||||
data[y * tw + x] = c;
|
||||
}
|
||||
}
|
||||
|
||||
imlib_context_set_drawable(pix);
|
||||
Imlib_Image img = imlib_create_image_from_drawable(0, 0, 0, tw, th, 1);
|
||||
//Imlib_Image img = imlib_create_image_using_data(tw, th, data);
|
||||
imlib_context_set_image(img);
|
||||
char path[256];
|
||||
static unsigned count = 0;
|
||||
sprintf(path, "thumb-%u-%u.png", win, count++);
|
||||
imlib_save_image(path);
|
||||
imlib_free_image();
|
||||
|
||||
// Convert to argb32
|
||||
if (rmask & 0xff0000) {
|
||||
// argb32 or rgb24 => Nothing to do
|
||||
@@ -539,24 +548,23 @@ cairo_surface_t *get_window_thumbnail_ximage(Window win, size_t size, gboolean u
|
||||
}
|
||||
|
||||
// 2nd pass
|
||||
smooth_thumbnail(result);
|
||||
//smooth_thumbnail(result);
|
||||
cairo_surface_flush(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:
|
||||
err4:
|
||||
if (ximg)
|
||||
XDestroyImage(ximg);
|
||||
if (src_pic)
|
||||
XRenderFreePicture(server.display, src_pic);
|
||||
if (dst_pic)
|
||||
XRenderFreePicture(server.display, dst_pic);
|
||||
if (pix)
|
||||
XFreePixmap(server.display, pix);
|
||||
err0:
|
||||
return result;
|
||||
}
|
||||
@@ -576,24 +584,11 @@ gboolean cairo_surface_is_blank(cairo_surface_t *image_surface)
|
||||
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)) {
|
||||
fprintf(stderr, RED "tint2: captured blank thumbnail" RESET "\n");
|
||||
cairo_surface_destroy(image_surface);
|
||||
image_surface = NULL;
|
||||
}
|
||||
|
||||
@@ -201,7 +201,7 @@ clock_dwheel_command =
|
||||
# Battery
|
||||
battery_tooltip = 1
|
||||
battery_low_status = 10
|
||||
battery_low_cmd = notify-send "battery low"
|
||||
battery_low_cmd = xmessage 'tint2: Battery low!'
|
||||
battery_full_cmd =
|
||||
bat1_font = sans 8
|
||||
bat2_font = sans 6
|
||||
|
||||
@@ -201,7 +201,7 @@ clock_dwheel_command =
|
||||
# Battery
|
||||
battery_tooltip = 1
|
||||
battery_low_status = 10
|
||||
battery_low_cmd = notify-send "battery low"
|
||||
battery_low_cmd = xmessage 'tint2: Battery low!'
|
||||
battery_full_cmd =
|
||||
bat1_font = sans 8
|
||||
bat2_font = sans 6
|
||||
|
||||
@@ -212,7 +212,7 @@ clock_dwheel_command =
|
||||
# Battery
|
||||
battery_tooltip = 1
|
||||
battery_low_status = 10
|
||||
battery_low_cmd = notify-send "battery low"
|
||||
battery_low_cmd = xmessage 'tint2: Battery low!'
|
||||
battery_full_cmd =
|
||||
bat1_font = sans 8
|
||||
bat2_font = sans 6
|
||||
|
||||
@@ -201,7 +201,7 @@ clock_dwheel_command =
|
||||
# Battery
|
||||
battery_tooltip = 1
|
||||
battery_low_status = 10
|
||||
battery_low_cmd = notify-send "battery low"
|
||||
battery_low_cmd = xmessage 'tint2: Battery low!'
|
||||
battery_full_cmd =
|
||||
bat1_font = sans 8
|
||||
bat2_font = sans 6
|
||||
|
||||
+1
-1
@@ -175,7 +175,7 @@ clock_dwheel_command =
|
||||
# Battery
|
||||
battery_tooltip = 1
|
||||
battery_low_status = 10
|
||||
battery_low_cmd = notify-send "battery low"
|
||||
battery_low_cmd = xmessage 'tint2: Battery low!'
|
||||
battery_full_cmd =
|
||||
battery_font_color = #ffffff 100
|
||||
bat1_format =
|
||||
|
||||
@@ -201,7 +201,7 @@ clock_dwheel_command =
|
||||
# Battery
|
||||
battery_tooltip = 1
|
||||
battery_low_status = 10
|
||||
battery_low_cmd = notify-send "battery low"
|
||||
battery_low_cmd = xmessage 'tint2: Battery low!'
|
||||
battery_full_cmd =
|
||||
bat1_font = sans 8
|
||||
bat2_font = sans 6
|
||||
|
||||
@@ -201,7 +201,7 @@ clock_dwheel_command =
|
||||
# Battery
|
||||
battery_tooltip = 1
|
||||
battery_low_status = 10
|
||||
battery_low_cmd = notify-send "battery low"
|
||||
battery_low_cmd = xmessage 'tint2: Battery low!'
|
||||
battery_full_cmd =
|
||||
bat1_font = sans 8
|
||||
bat2_font = sans 6
|
||||
|
||||
@@ -212,7 +212,7 @@ clock_dwheel_command =
|
||||
# Battery
|
||||
battery_tooltip = 1
|
||||
battery_low_status = 10
|
||||
battery_low_cmd = notify-send "battery low"
|
||||
battery_low_cmd = xmessage 'tint2: Battery low!'
|
||||
battery_full_cmd =
|
||||
bat1_font = sans 8
|
||||
bat2_font = sans 6
|
||||
|
||||
@@ -201,7 +201,7 @@ clock_dwheel_command =
|
||||
# Battery
|
||||
battery_tooltip = 1
|
||||
battery_low_status = 10
|
||||
battery_low_cmd = notify-send "battery low"
|
||||
battery_low_cmd = xmessage 'tint2: Battery low!'
|
||||
battery_full_cmd =
|
||||
bat1_font = sans 8
|
||||
bat2_font = sans 6
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
configure
|
||||
doc/tint2.1
|
||||
sample/icon_and_text_1.tint2rc
|
||||
sample/icon_and_text_2.tint2rc
|
||||
@@ -59,6 +60,8 @@ src/util/area.c
|
||||
src/util/area.h
|
||||
src/util/blur.c
|
||||
src/util/blur.h
|
||||
src/util/bt.c
|
||||
src/util/bt.h
|
||||
src/util/common.c
|
||||
src/util/common.h
|
||||
src/util/server.c
|
||||
|
||||
Reference in New Issue
Block a user