Initial checkin of Pika from heckimp

This commit is contained in:
2023-09-25 15:35:21 -07:00
commit 891e999216
6761 changed files with 5240685 additions and 0 deletions

88
tools/colorsvg2png.c Normal file
View File

@ -0,0 +1,88 @@
/* colorsvg2png.c
* Copyright (C) 2018 Jehan
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#include <glib/gprintf.h>
#include <librsvg/rsvg.h>
int
main (int argc, char **argv)
{
GError *error = NULL;
RsvgHandle *handle;
cairo_surface_t *surface;
cairo_t *cr;
RsvgRectangle target_rect;
gchar *input;
gchar *output;
gint dim;
gint retval = 0;
if (argc != 4)
{
g_fprintf (stderr, "Usage: colorsvg2png svg-image png-output size\n");
return 1;
}
input = argv[1];
output = argv[2];
dim = (gint) g_ascii_strtoull (argv[3], NULL, 10);
if (dim < 1)
{
g_fprintf (stderr, "Usage: invalid dimension %d\n", dim);
return 1;
}
handle = rsvg_handle_new_from_file (input, &error);
if (! handle)
{
g_fprintf (stderr,
"Error: failed to load '%s' as SVG: %s\n",
input, error->message);
g_error_free (error);
return 1;
}
surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, dim, dim);
cr = cairo_create (surface);
target_rect.x = target_rect.y = 0;
target_rect.width = target_rect.height = dim;
if (! rsvg_handle_render_document (handle, cr, &target_rect, NULL))
{
g_fprintf (stderr,
"Error: failed to render '%s'\n",
input);
retval = 1;
}
if (retval == 0 &&
cairo_surface_write_to_png (surface, output) != CAIRO_STATUS_SUCCESS)
{
g_fprintf (stderr,
"Error: failed to write '%s'\n",
output);
retval = 1;
}
cairo_surface_destroy (surface);
cairo_destroy (cr);
g_object_unref (handle);
return retval;
}

View File

@ -0,0 +1,86 @@
/* compute-svg-viewbox.c
* Copyright (C) 2016 Jehan
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#include <librsvg/rsvg.h>
#include <stdio.h>
#include <stdlib.h>
int main (int argc, char **argv)
{
RsvgHandle *handle;
RsvgRectangle viewport = { 0.0, 0.0, 16.0, 16.0 };
RsvgRectangle out_ink_rect;
RsvgRectangle out_logical_rect;
gchar *endptr;
gchar *path;
gchar *id;
gint prev_x;
gint prev_y;
if (argc != 5)
{
fprintf (stderr, "Usage: compute-svg-viewbox path object_id x y\n");
return 1;
}
prev_x = strtol (argv[3], &endptr, 10);
if (endptr == argv[3])
{
fprintf (stderr, "Error: the third parameter must be an integer\n");
return 1;
}
prev_y = strtol (argv[4], &endptr, 10);
if (endptr == argv[4])
{
fprintf (stderr, "Error: the fourth parameter must be an integer\n");
return 1;
}
path = argv[1];
handle = rsvg_handle_new_from_file (path, NULL);
if (! handle)
{
fprintf (stderr, "Error: wrong path \"%s\".\n", path);
return 1;
}
id = g_strdup_printf ("#%s", argv[2]);
if (! rsvg_handle_has_sub (handle, id))
{
fprintf (stderr, "Error: the id \"%s\" does not exist.\n", id);
g_object_unref (handle);
g_free (id);
return 1;
}
rsvg_handle_get_geometry_for_layer (handle, id, &viewport, &out_ink_rect, &out_logical_rect, NULL);
if (out_ink_rect.width != out_ink_rect.height)
{
/* Right now, we are constraining all objects into square objects. */
fprintf (stderr, "WARNING: object \"%s\" has unexpected size %fx%f [pos: (%f, %f)].\n",
id, out_ink_rect.width, out_ink_rect.height,
out_ink_rect.x, out_ink_rect.y);
}
printf ("viewBox=\"%f %f %f %f\"",
out_ink_rect.x + prev_x, out_ink_rect.y + prev_y,
out_ink_rect.width, out_ink_rect.height);
g_object_unref (handle);
g_free (id);
return 0;
}

131
tools/defcheck.py Normal file
View File

@ -0,0 +1,131 @@
#!/usr/bin/env python3
"""
defcheck.py -- Consistency check for the .def files.
Copyright (C) 2006 Simon Budig <simon@gimp.org>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
This is a hack to check the consistency of the .def files compared to
the respective libraries.
Invoke in the top level of the pika source tree after compiling PIKA.
If srcdir != builddir, run it in the build directory and pass the name
of the source directory on the command-line.
Needs the tool "nm" to work
"""
import sys, subprocess
from os import path
def_files = (
"libpikabase/pikabase.def",
"libpikacolor/pikacolor.def",
"libpikaconfig/pikaconfig.def",
"libpika/pika.def",
"libpika/pikaui.def",
"libpikamath/pikamath.def",
"libpikamodule/pikamodule.def",
"libpikathumb/pikathumb.def",
"libpikawidgets/pikawidgets.def"
)
have_errors = 0
srcdir = None
if len(sys.argv) > 1:
srcdir = sys.argv[1]
if not path.exists(srcdir):
print("Directory '%s' does not exist" % srcdir)
sys.exit (-1)
for df in def_files:
directory, name = path.split (df)
basename, extension = name.split (".")
libname = path.join(directory, ".libs", "lib" + basename + "-*.so")
filename = df
if srcdir:
filename = path.join(srcdir, df)
try:
defsymbols = open (filename).read ().split ()[1:]
except IOError as message:
print(message)
if not srcdir:
print("You should run this script from the toplevel source directory.")
sys.exit (-1)
doublesymbols = []
for i in range (len (defsymbols)-1, 0, -1):
if defsymbols[i] in defsymbols[:i]:
doublesymbols.append ((defsymbols[i], i+2))
unsortindex = -1
for i in range (len (defsymbols)-1):
if defsymbols[i] > defsymbols[i+1]:
unsortindex = i+1
break;
status, nm = subprocess.getstatusoutput ("nm --defined-only --extern-only " +
libname)
if status != 0:
libname_meson = path.join(directory, "lib" + basename + "-*.so")
status, nm = subprocess.getstatusoutput ("nm --defined-only --extern-only " +
libname_meson)
if status != 0:
print("trouble reading {} or {} - has it been compiled?".format(libname, libname_meson))
have_errors = -1
continue
nmsymbols = nm.split()[2::3]
nmsymbols = [s for s in nmsymbols if s[0] != '_']
missing_defs = [s for s in nmsymbols if s not in defsymbols]
missing_nms = [s for s in defsymbols if s not in nmsymbols]
if unsortindex >= 0 or missing_defs or missing_nms or doublesymbols:
print()
print("Problem found in", filename)
if missing_defs:
print(" the following symbols are in the library,")
print(" but are not listed in the .def-file:")
for s in missing_defs:
print(" +", s)
print()
if missing_nms:
print(" the following symbols are listed in the .def-file,")
print(" but are not exported by the library.")
for s in missing_nms:
print(" -", s)
print()
if doublesymbols:
print(" the following symbols are listed multiple times in the .def-file,")
for s in doublesymbols:
print(" : %s (line %d)" % s)
print()
if unsortindex >= 0:
print(" the .def-file is not properly sorted (line %d)" % (unsortindex + 2))
print()
have_errors = -1
sys.exit (have_errors)

View File

@ -0,0 +1,121 @@
#!/bin/sh
# extract-vector-icon.sh
# Copyright (C) 2016 Jehan
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <https://www.gnu.org/licenses/>.
# This script generates a new SVG file by extracting a single object by
# its id, from a source SVG, and updating the viewBox (canvas) size and
# position.
usage ()
{
printf "Usage: extract-vector-icon.sh source icon-name [width height]\n"
printf "Create the file 'icon-name.svg' from the \`source\` SVG.\n"
}
if [ "$#" != 2 ]; then
if [ "$#" != 4 ]; then
usage
exit 1
fi
fi
# The script is run from $(top_builddir)/icons/*/
compute_viewbox="$(pwd)/../../tools/compute-svg-viewbox"
source="$1"
id="$2"
if [ "$#" = 4 ]; then
# The expected display width/height for the image.
width="$3"
height="$4"
else
# We base the design of our scalable icons on 16x16 pixels.
width="16"
height="16"
fi
# Extract the icon code.
#icon=`xmllint "$source" --xpath '//*[local-name()="g" and @id="'$id'"]'`
icon=`xmllint "$source" --xpath '//*[@id="'$id'"]' --noblanks`
# Get rid of any transform on the top node to help librsvg.
#icon=`echo $icon | sed 's/^\(<[^>]*\) transform="[^"]*"/\1/'`
if [ $? -ne 0 ]; then
>&2 echo "extract-vector-icon.sh: object id \"$id\" not found in \"$source\" ";
exit 1;
fi;
# Add !important to any object with label "color-important".
icon=`echo $icon | sed 's/<\([^<>]*\)style="\([^"]*\)fill:\([^;"]*\)\([^"]*\)"\([^<>]*\)inkscape:label="color-important"\([^>]*\)>/<\1style="\2fill:\3 !important\4"\5\6>/'`
icon=`echo $icon | sed 's/<\([^<>]*\)inkscape:label="color-important"\([^>]*\)style="\([^"]*\)fill:\([^;"]*\)\([^"]*\)"\([^<>]*\)>/<\1\2style="\3fill:\4 !important\5"\6>/'`
# The typical namespaces declared at start of a SVG made with Inkscape.
# Since we are not sure of what namespace will use the object XML, and
# since we don't want to end up with invalid XML, we just keep them all
# declared here.
svg_start='<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created by PIKA build. -->
<svg
xmlns:osb="http://www.openswatchbook.org/uri/2009/osb"
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
version="1.1"
'
# Grab the defined color palette.
defs=`xmllint "$source" --xpath '//*[local-name()="defs"]'`
# Create a temporary SVG file with the main information.
svg_temp="`mktemp ./${id}-XXXX.svg`"
echo "$svg_start>$defs$icon</svg>" > $svg_temp
x=0
y=0
# In case the source SVG has a viewBox not starting at (0, 0), get
# the current origin coordinates.
#viewBox=`xmllint $svg_temp --xpath '/*[local-name()="svg"]/@viewBox'`
#if [ $? -eq 0 ]; then
# x=`echo $viewBox| sed 's/ *viewBox *= *"\([0-9]\+\) .*$/\1/'`
# y=`echo $viewBox| sed 's/ *viewBox *= *"[0-9]\+ \+\([0-9]\+\).*$/\1/'`
#fi;
# Compute the viewBox that we want to set to our generated SVG.
viewBox=`$compute_viewbox "$svg_temp" "$id" $x $y`
if [ $? -ne 0 ]; then
>&2 echo "extract-vector-icon.sh: error running \`$compute_viewbox "$tmp" "$id" $x $y\`.";
rm -f $svg_temp
exit 1;
fi;
rm -f $svg_temp
# The finale SVG file with properly set viewBox.
svg="$svg_start $viewBox"
if [ "$#" = 5 ]; then
svg="$svg
width=\"$width\"
height=\"$height\""
fi
svg="$svg>
<title>$id</title>
$defs
$icon
</svg>"
echo "$svg"

186
tools/flatpak-releases Normal file
View File

@ -0,0 +1,186 @@
#!/bin/bash
# This is a very basic script for developper usage. It has a few known
# limitations (feel free to send patches for these):
# - It is targetted at PIKA usage primarily, hence able to check only
# Flathub (stable and beta remotes) and GNOME-nightly. Yet some
# generity is built-in so you can set your own application ID on
# command line and it should work.
# - It assumes the remotes are named 'flathub', 'flathub-beta' and
# 'gnome-nightly' (for stable, beta and nightly branches respectively)
# as these are the default names proposed by generated .flatpakref
# files (SuggestRemoteName field) when first installing software from
# this repository. So most people will have these remotes registered
# with these names. Yet it could technically be any name locally and
# this script is not verifying this.
# - It also assumes the flathub remotes are installed at all (it can't
# search without them being installed and won't install these for
# you).
# - It uses bash because I lazily didn't bother making it portable as
# it's really just a tool for core dev testing. Yet we of course
# welcome patches if some syntax needs to be rewritten for
# portability.
install=-1
show_runtime=0
appid=technology.heckin.PIKA
remote='flathub'
branch='stable'
prefix='--user'
for var in "$@"
do
if [[ $var =~ ^-([0-9]+)$ ]]; then
install=${BASH_REMATCH[1]}
elif [[ $var = '--beta' ]]; then
remote='flathub-beta'
branch='beta'
elif [[ $var = '--nightly' ]]; then
remote='gnome-nightly'
branch='master'
elif [[ $var = '--system' ]]; then
prefix='--system'
elif [[ $var = '--runtime' ]]; then
show_runtime=1
elif [[ $var =~ ^- ]]; then
echo "Usage: ./flathub-releases [--beta] [--system] [-X] [org.example.app]"
echo
echo "List all flatpak builds stored on Flathub or GNOME repository."
echo "The builds for technology.heckin.PIKA are looked up by default unless"
echo "you provide explicitely another AppStream ID."
echo
echo "Adding a -X number as option install the numbered release"
echo "instead of listing them."
echo
echo "Options:"
echo
echo "-0: install the last build."
echo "-1: install the previous build."
echo "-2: install the before-previous build (and so on)."
echo "-[0-9]+: and so on..."
echo
echo "--beta: list or install a beta release"
echo "--nightly: list or install a nightly release"
echo
echo "--runtime: list or install runtimes (can be associated with --beta and --nightly)"
echo
echo "--system: install as system flatpak (default to user install)"
echo
echo "Examples:"
echo
echo "* List all beta flatpak bulds:"
echo " flathub-releases --beta"
echo "* Install 2-build old beta flatpak:"
echo "* flathub-releases --beta -2"
echo "* Install the latest beta flatpak:"
echo "* flathub-releases --beta -0"
echo "* List all builds of the runtime used by the beta flatpak:"
echo "* flathub-releases --beta --runtime"
echo "* Install the previous runtime build to be used by the beta flatpak:"
echo "* flathub-releases --beta --runtime -1"
exit 1
else
appid=$var
fi
done
package_info_cmd="flatpak remote-info $remote $appid"
package_info=`$package_info_cmd 2>&1`
got_info="$?"
commit_prefix="app"
if [ "$got_info" -ne 0 ]; then
# By default flatpak will just use either the user or system install
# depending on what it finds. Funnily the command may fail if the
# remote is found not 0 or 1 but 2 times (both on user and system).
# Normally it would interactively ask to choose, but in this specific
# non-interactive case, it would just silently fail instead. So let's
# make a second try on user-installed remote (totally arbitrary
# choice).
user_system="--user"
package_info_cmd="flatpak remote-info $user_system $remote $appid"
package_info=`$package_info_cmd 2>&1`
got_info="$?"
fi
if [ "$got_info" -ne 0 ]; then
echo "Flathub query failed with the following error: $package_info"
exit 2
elif [ "$show_runtime" -eq 1 ]; then
# With the special --runtime option, we list the associated runtime instead of
# the application.
runtime=`echo "$package_info" | grep Runtime: |sed 's/^ *Runtime: //'`
appid=$runtime
# The beta runtime is in the stable repository.
if [[ $branch = 'beta' ]]; then
remote='flathub'
fi
package_info_cmd="flatpak remote-info $user_system $remote $appid//$branch"
package_info=`$package_info_cmd 2>&1`
got_info="$?"
if [ "$got_info" -ne 0 ]; then
if [ -z "$user_system" ]; then
# Do the user/system dance again. Previously we were doing this about the
# main package, not its runtime.
user_system="--user"
package_info_cmd="flatpak remote-info $user_system $remote $appid//$branch"
package_info=`$package_info_cmd 2>&1`
got_info="$?"
fi
if [ "$got_info" -ne 0 ]; then
echo "Flathub query failed with the following error: $package_info"
exit 2
fi
fi
commit_prefix="runtime"
fi
release_number=0
install_commit=
while [ "$got_info" -eq 0 ]
do
release_date=`echo "$package_info" | grep Date: |sed 's/^ *Date: //'`
release_commit=`echo "$package_info" | grep Commit: |sed 's/^ *Commit: //'`
release_subject=`echo "$package_info" | grep Subject: |sed 's/^ *Subject: //'`
if [ "$install" -eq -1 ]; then
# In non-install mode, just list the whole release.
printf "%2d: %s [%s] - $commit_prefix-commit: %s\n" $release_number "$release_subject" "$release_date" "$release_commit"
elif [ "$install" -eq "$release_number" ]; then
install_commit=$release_commit
break
fi
parent_commit=`echo "$package_info" | grep Parent: |sed 's/^ *Parent: //'`
release_number=$(( $release_number + 1 ))
package_info=`$package_info_cmd --commit $parent_commit 2>&1`
got_info="$?"
done
if [ "$install" -ne -1 ]; then
if [ -n "$install_commit" ]; then
# Flatpak doesn't have a way to install directly a commit, but we
# can install then update. This will work whether the flatpak is
# already installed or not.
echo "[1/2] Installing $appid"
flatpak install -y $prefix $remote $appid//$branch
echo "[2/2] Updating to commit '$install_commit' ($release_number's previous build)"
flatpak update -y $prefix --commit=$install_commit $appid//$branch
if [ "$?" -eq 0 ]; then
echo "Build $release_number released on $release_date was installed."
echo "Build subject: $release_subject"
echo "Build commit on $remote: $release_commit"
else
echo "Failure to install build $release_number released on $release_date."
exit 2
fi
else
echo "There was no $release_number's build to install. Aborting."
exit 1
fi
fi
exit 0

View File

@ -0,0 +1,229 @@
#!/usr/bin/env python3
"""
generate-icon-makefiles.py -- Generate icons/(Color|Symbolic)/icon-list.mk
Copyright (C) 2022 Jehan
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
Usage: generate-icon-makefiles.py
"""
import os.path
tools_dir = os.path.dirname(os.path.realpath(__file__))
icons_dir = os.path.join(tools_dir, '../icons')
list_dir = os.path.join(icons_dir, 'icon-lists')
color_mk = os.path.join(icons_dir, 'Color', 'icon-list.mk')
symbolic_mk = os.path.join(icons_dir, 'Symbolic', 'icon-list.mk')
def print_icons(indir, filenames, max_len, prefix, suffix, outfile, endlist=True):
icons = []
for filename in filenames:
icon_list = os.path.join(indir, filename)
with open(icon_list, mode='r') as f:
icons += [line.strip() for line in f]
# Replace comment lines with empty strings.
prev_blank = False
pop_list = []
for i, icon in enumerate(icons):
if icon != '' and icon[0] == '#':
icons[i] = ''
if icons[i] == '':
if prev_blank:
pop_list += [i]
prev_blank = True
else:
prev_blank = False
if icons[i] in icons[:i]:
pop_list += [i]
pop_list.reverse()
for i in pop_list:
# Remove successive blanks and duplicate icons.
icons.pop(i)
# Strip empty lines in extremities.
while icons[-1] == '':
icons.pop()
while icons[0] == '':
icons.pop(0)
if max_len is None:
max_len = len(max(icons, key=len)) + len(prefix + suffix)
prev_empty = False
# Using tabs, displayed as 8 chars in our coding style. Computing
# needed tabs for proper alignment.
needed_tabs = int(max_len / 8) + (1 if max_len % 8 != 0 else 0)
for icon in icons[:-1]:
if icon == '':
# Only keep one empty line.
if not prev_empty:
print("\t\\", file=outfile)
prev_empty = True
else:
icon_path = prefix + icon + suffix
tab_mult = needed_tabs - int(len(icon_path) / 8) + 1
icon_path = "\t{}{}\\".format(icon_path, "\t" * tab_mult)
print(icon_path, file=outfile)
prev_empty = False
else:
if endlist:
icon_path = "\t{}".format(prefix) + icons[-1] + suffix
print(icon_path, file=outfile)
else:
icon_path = prefix + icons[-1] + suffix
tab_mult = needed_tabs - int(len(icon_path) / 8) + 1
icon_path = "\t{}{}\\".format(icon_path, "\t" * tab_mult)
print(icon_path, file=outfile)
return max_len
if __name__ == "__main__":
with open(color_mk, mode='w') as colorf, open(symbolic_mk, mode='w') as symbolicf:
top_comment = '''
## --------------------------------------------------------------
## This file is autogenerated by tools/generate-icon-makefiles.py
## --------------------------------------------------------------
## Modify this script or files inside icons/icon-lists/ instead of this one.
## Then run tools/generate-icon-makefiles.py again.
'''
print(top_comment, file=colorf)
print(top_comment, file=symbolicf)
# Scalable icons.
print("scalable_images = \\", file=colorf)
print("scalable_images = \\", file=symbolicf)
# Let's assume that scalable icons are the biggest list since it
# should contain nearly all images. So we compute max_len once and
# reuse this value on all lists.
col_max_len = print_icons(list_dir,
['scalable.list',
'color-selectors.list', 'controllers.list', 'display-filters.list',
'locks.list', 'prefs.list', 'templates.list', 'tools.list'],
None, "scalable/", ".svg", colorf)
sym_max_len = print_icons(list_dir,
['scalable.list',
'color-selectors.list', 'controllers.list', 'display-filters.list',
'locks.list', 'prefs.list', 'templates.list', 'tools.list'],
None, "scalable/", "-symbolic.svg", symbolicf)
# 12x12 bitmap
print("\nicons12_images = \\", file=colorf)
print("\nicons12_images = \\", file=symbolicf)
print_icons(list_dir, ['bitmap_12.list'], col_max_len, "12/", ".png", colorf)
print_icons(list_dir, ['bitmap_12.list'], sym_max_len, "12/", "-symbolic.symbolic.png", symbolicf)
# 16x16 bitmap
print("\nicons16_images = \\", file=colorf)
print("\nicons16_images = \\", file=symbolicf)
print_icons(list_dir,
['bitmap_16.list',
'color-selectors.list', 'controllers.list', 'display-filters.list',
'locks.list', 'prefs.list', 'templates.list', 'tools.list'],
col_max_len, "16/", ".png", colorf)
print_icons(list_dir,
['bitmap_16.list',
'color-selectors.list', 'controllers.list', 'display-filters.list',
'locks.list', 'prefs.list', 'templates.list', 'tools.list'],
sym_max_len, "16/", "-symbolic.symbolic.png", symbolicf)
# 18x18 bitmap
print("\nicons18_images = \\", file=colorf)
print("\nicons18_images = \\", file=symbolicf)
print_icons(list_dir, ['bitmap_18.list'], col_max_len, "18/", ".png", colorf)
print_icons(list_dir, ['bitmap_18.list'], sym_max_len, "18/", "-symbolic.symbolic.png", symbolicf)
# 22x22 bitmap
print("\nicons22_images = \\", file=colorf)
print("\nicons22_images = \\", file=symbolicf)
print_icons(list_dir, ['bitmap_22.list'], col_max_len, "22/", ".png", colorf)
print_icons(list_dir, ['bitmap_22.list'], sym_max_len, "22/", "-symbolic.symbolic.png", symbolicf)
# 24x24 bitmap
print("\nicons24_images = \\", file=colorf)
print("\nicons24_images = \\", file=symbolicf)
print_icons(list_dir, ['bitmap_24.list', 'templates.list', 'tools.list'],
col_max_len, "24/", ".png", colorf)
print_icons(list_dir, ['bitmap_24.list', 'templates.list', 'tools.list'],
sym_max_len, "24/", "-symbolic.symbolic.png", symbolicf)
# 32x32 bitmap
print("\nicons32_images = \\", file=colorf)
print("\nicons32_images = \\", file=symbolicf)
print_icons(list_dir, ['bitmap_32.list'], col_max_len, "32/", ".png", colorf)
print_icons(list_dir, ['bitmap_32.list'], sym_max_len, "32/", "-symbolic.symbolic.png", symbolicf)
# 48x48 bitmap
print("\nicons48_images = \\", file=colorf)
print("\nicons48_images = \\", file=symbolicf)
print_icons(list_dir, ['bitmap_48.list', 'prefs.list'], col_max_len, "48/", ".png", colorf)
print_icons(list_dir, ['bitmap_48.list', 'prefs.list'], sym_max_len, "48/", "-symbolic.symbolic.png", symbolicf)
# 64x64 bitmap
print("\nicons64_images = \\", file=colorf)
print("\nicons64_images = \\", file=symbolicf)
print_icons(list_dir, [ 'bitmap_64-always.list', 'bitmap_64.list'], col_max_len, "64/", ".png", colorf)
print_icons(list_dir, [ 'bitmap_64-always.list' ], sym_max_len, "64/", ".png", symbolicf, endlist=False)
print_icons(list_dir, [ 'bitmap_64.list'], sym_max_len, "64/", "-symbolic.symbolic.png", symbolicf)
print("\nicons64_system_images = \\", file=colorf)
print("\nicons64_system_images = \\", file=symbolicf)
print_icons(list_dir, ['bitmap_64-system.list'], col_max_len, "64/", ".png", colorf)
print_icons(list_dir, ['bitmap_64-system.list'], sym_max_len, "64/", "-symbolic.symbolic.png", symbolicf)
# 96x96 bitmap
print("\nicons96_images = \\", file=colorf)
print("\nicons96_images = \\", file=symbolicf)
print_icons(list_dir, ['bitmap_96.list'], col_max_len, "96/", ".png", colorf)
print_icons(list_dir, ['bitmap_96.list'], sym_max_len, "96/", "-symbolic.symbolic.png", symbolicf)
# 128x128 bitmap
print("\nicons128_images = \\", file=colorf)
print("\nicons128_images = \\", file=symbolicf)
print_icons(list_dir, ['bitmap_128.list'], col_max_len, "128/", ".png", colorf)
print_icons(list_dir, ['bitmap_128.list'], sym_max_len, "128/", "-symbolic.symbolic.png", symbolicf)
# 192x192 bitmap
print("\nicons192_images = \\", file=colorf)
print("\nicons192_images = \\", file=symbolicf)
print_icons(list_dir, ['bitmap_192.list'], col_max_len, "192/", ".png", colorf)
print_icons(list_dir, ['bitmap_192.list'], sym_max_len, "192/", "-symbolic.symbolic.png", symbolicf)
# 256x256 bitmap
print("\nicons256_images = \\", file=colorf)
print("\nicons256_images = \\", file=symbolicf)
print_icons(list_dir, ['bitmap_256.list'], col_max_len, "256/", ".png", colorf)
print_icons(list_dir, ['bitmap_256.list'], sym_max_len, "256/", "-symbolic.symbolic.png", symbolicf)
print(file=colorf)
print(file=symbolicf)
eof = os.path.join(list_dir, "icon-list.mk.eof")
with open(eof, mode='r') as f:
for line in f:
colorf.write(line)
symbolicf.write(line)
# Touch the 2 meson.build to force-trigger their re-processing (hence
# re-configuration) at next build. Otherwise even with image list
# changed, meson might not see it as it uses the list from the last
# configuration.
os.utime(os.path.join(icons_dir, 'Color', 'meson.build'), times=None)
os.utime(os.path.join(icons_dir, 'Symbolic', 'meson.build'), times=None)

30
tools/generate-news Normal file
View File

@ -0,0 +1,30 @@
#!/bin/bash
# Copyright (C) 2015 Ville Pätsi <drc@gimp.org>
SCRIPT_FOLDER=$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )
FIRST_COMMIT="$1"
[ -z "$FIRST_COMMIT" ] && FIRST_COMMIT="950412fbdc720fe2600f58f04f25145d9073895d" # First after tag 2.8.0
declare -a FOLDERS=('app tools menus etc' \
'libpika libpikabase libpikacolor libpikaconfig libpikamath libpikamodule libpikathumb libpikawidgets' \
'plug-ins' \
'modules'
'build' \
'themes icons')
OUTPUTFILE=${SCRIPT_FOLDER}/../NEWS_since_"${FIRST_COMMIT}"
pushd ${SCRIPT_FOLDER}/..
for folderloop in "${FOLDERS[@]}"
do uppercase_folderloop="`echo ${folderloop:0:1} | tr '[:lower:]' '[:upper:]'`${folderloop:1}"
echo -e "${uppercase_folderloop}:\n" >> "${OUTPUTFILE}"
git log --date-order --reverse --date=short --pretty=format:"- %h %s" "${FIRST_COMMIT}"..HEAD ${folderloop} >> "${OUTPUTFILE}"
echo -e "\n\n" >> "${OUTPUTFILE}"
done
popd
echo "NEWS generated into ${OUTPUTFILE}"

View File

@ -0,0 +1,154 @@
#!/usr/bin/env python3
"""
generate-welcome-dialog-data.py -- Generate app/dialogs/welcome-dialog-data.h
Copyright (C) 2022 Jehan
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
Usage: generate-welcome-dialog-data.py
"""
import argparse
import os.path
import re
import sys
import xml.etree.ElementTree as ET
tools_dir = os.path.dirname(os.path.realpath(__file__))
desktop_dir = os.path.join(tools_dir, '../desktop')
outdir = os.path.join(tools_dir, '../app/dialogs')
infile = os.path.join(desktop_dir, 'technology.heckin.PIKA.appdata.xml.in.in')
outfile = os.path.join(outdir, 'welcome-dialog-data.h')
def parse_appdata(infile, version):
introduction = []
release_texts = []
release_demos = []
spaces = re.compile(r'\s+')
tree = ET.parse(infile)
root = tree.getroot()
releases_node = root.find('releases')
releases = releases_node.findall('release')
for release in releases:
if 'version' in release.attrib and release.attrib['version'] == version:
intro = release.findall('./description/p')
for p in intro:
# Naive conversion for C strings, but it will probably fit for
# most cases.
p = p.text.strip()
p = p.replace('\\', '\\\\')
p = p.replace('"', '\\"')
# All redundant spaces unwanted as XML merges them anyway.
introduction += [spaces.sub(' ', p)]
items = release.findall('./description/ul/li')
for item in items:
text = item.text.strip()
text = text.replace('\\', '\\\\')
text = text.replace('"', '\\"')
demo = None
if 'demo' in item.attrib:
demo = item.attrib['demo']
# All spaces unneeded in demo string.
demo = demo.replace(' ', '')
release_texts += [spaces.sub(' ', text)]
release_demos += [demo]
break
return introduction, release_texts, release_demos
if __name__ == "__main__":
parser = argparse.ArgumentParser()
parser.add_argument('version')
parser.add_argument('--header', action='store_true')
args = parser.parse_args(sys.argv[1:])
top_comment = '''/* PIKA - Photo and Image Kooker Application
* Copyright (C) 1995 Spencer Kimball and Peter Mattis
*
* welcome-dialog-data.h
* Copyright (C) 2022 Jehan
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*
***********************************************************************
* This file is autogenerated by tools/generate-welcome-dialog-data.py *
***********************************************************************
*
* Modify the python script or desktop/technology.heckin.PIKA.appdata.xml.in.in
* instead of this one
* Then run tools/generate-welcome-dialog-data.py again.
*/
'''
print(top_comment)
intro_p, items, demos = parse_appdata(infile, args.version)
if args.header:
print('#ifndef __WELCOME_DIALOG_DATA_H__')
print('#define __WELCOME_DIALOG_DATA_H__\n\n')
print('extern gint pika_welcome_dialog_n_items;')
print('extern const gchar * pika_welcome_dialog_items[];')
print('extern const gchar * pika_welcome_dialog_demos[];')
print()
print('extern gint pika_welcome_dialog_intro_n_paragraphs;')
print('extern const gchar * pika_welcome_dialog_intro[];')
print('\n\n#endif /* __WELCOME_DIALOG_DATA_H__ */')
else:
print('#include "config.h"')
print('#include <glib.h>')
print()
print('const gint pika_welcome_dialog_n_items = {};'.format(len(demos)))
print()
print('const gchar *pika_welcome_dialog_items[] =')
print('{')
for item in items:
print(' "{}",'.format(item))
print(' NULL,\n};')
print()
print('const gchar *pika_welcome_dialog_demos[] =')
print('{')
for demo in demos:
if demo is None:
print(' NULL,')
else:
print(' "{}",'.format(demo))
print(' NULL,\n};')
print()
print('const gint pika_welcome_dialog_intro_n_paragraphs = {};'.format(len(intro_p)))
print()
print('const gchar *pika_welcome_dialog_intro[] =')
print('{')
for p in intro_p:
print(' "{}",'.format(p))
print(' NULL,\n};')

View File

@ -0,0 +1,35 @@
#!/bin/bash
srcdir="$1"
output="$2"
echo "Creating ${output} based on git log"
gitdir="${srcdir}/.git"
if [[ ! -d "${gitdir}" ]]; then
echo "A git checkout and git-log is required to write changelog in ${output}." \
| tee ${output} >&2
exit 1
fi
CHANGELOG_START=74424325abb54620b370f2595445b2b2a19fe5e7
( \
git log "${CHANGELOG_START}^.." --stat "${srcdir}" | fmt --split-only \
> "${output}.tmp" \
&& [ ${PIPESTATUS[0]} -eq 0 ] \
&& mv "${output}.tmp" "${output}" -f \
&& echo "Appending ChangeLog.pre-git" \
&& cat "${srcdir}/ChangeLog.pre-git" >> "${output}" \
&& exit 0
) \
||\
( \
rm "${output}.tmp" -f \
&& echo "Failed to generate ChangeLog, your ChangeLog may be outdated" >&2 \
&& (test -f "${output}" \
|| echo "git-log is required to generate this file" >> "${output}") \
&& exit 1
)

132
tools/kernelgen.c Normal file
View File

@ -0,0 +1,132 @@
/* PIKA - Photo and Image Kooker Application
* a rebranding of The GNU Image Manipulation Program (created with heckimp)
* A derived work which may be trivial. However, any changes may be (C)2023 by Aldercone Studio
*
* Original copyright, applying to most contents (license remains unchanged):
* Copyright (C) 1995 Spencer Kimball and Peter Mattis
*
* kernelgen -- Copyright (C) 2000 Sven Neumann <sven@gimp.org>
*
* Simple hack to create brush subsampling kernels. If you want to
* play with it, change some of the #defines at the top and copy
* the output to app/paint/pikabrushcore-kernels.h.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#include <math.h>
#include <stdio.h>
#include <string.h>
#define STEPS 64
#define KERNEL_WIDTH 3 /* changing these makes no sense */
#define KERNEL_HEIGHT 3 /* changing these makes no sense */
#define KERNEL_SUM 256
#define SUBSAMPLE 4
#define THRESHOLD 0.25 /* try to change this one */
#define SQR(x) ((x) * (x))
static void
create_kernel (double x,
double y)
{
double value[KERNEL_WIDTH][KERNEL_HEIGHT];
double dist_x;
double dist_y;
double sum;
double w;
int i, j;
memset (value, 0, KERNEL_WIDTH * KERNEL_HEIGHT * sizeof (double));
sum = 0.0;
x += 1.0;
y += 1.0;
for (j = 0; j < STEPS * KERNEL_HEIGHT; j++)
{
dist_y = y - (((double)j + 0.5) / (double)STEPS);
for (i = 0; i < STEPS * KERNEL_WIDTH; i++)
{
dist_x = x - (((double) i + 0.5) / (double) STEPS);
/* I've tried to use a gauss function here instead of a
threshold, but the result was not that impressive. */
w = (SQR (dist_x) + SQR (dist_y)) < THRESHOLD ? 1.0 : 0.0;
value[i / STEPS][j / STEPS] += w;
sum += w;
}
}
for (j = 0; j < KERNEL_HEIGHT; j++)
{
for (i = 0; i < KERNEL_WIDTH; i++)
{
w = (double) KERNEL_SUM * value[i][j] / sum;
printf (" %3d,", (int) (w + 0.5));
}
}
}
int
main (int argc,
char **argv)
{
int i, j;
double x, y;
printf ("/* pikabrushcore-kernels.h\n"
" *\n"
" * This file was generated using kernelgen as found in the tools dir.\n");
printf (" * (threshold = %g)\n", THRESHOLD);
printf (" */\n\n");
printf ("#ifndef __PIKA_BRUSH_CORE_KERNELS_H__\n");
printf ("#define __PIKA_BRUSH_CORE_KERNELS_H__\n\n");
printf ("#define KERNEL_WIDTH %d\n", KERNEL_WIDTH);
printf ("#define KERNEL_HEIGHT %d\n", KERNEL_HEIGHT);
printf ("#define KERNEL_SUBSAMPLE %d\n", SUBSAMPLE);
printf ("#define KERNEL_SUM %d\n", KERNEL_SUM);
printf ("\n\n");
printf ("/* Brush pixel subsampling kernels */\n");
printf ("static const int subsample[%d][%d][%d] =\n{\n",
SUBSAMPLE + 1, SUBSAMPLE + 1, KERNEL_WIDTH * KERNEL_HEIGHT);
for (j = 0; j <= SUBSAMPLE; j++)
{
y = (double) j / (double) SUBSAMPLE;
printf (" {\n");
for (i = 0; i <= SUBSAMPLE; i++)
{
x = (double) i / (double) SUBSAMPLE;
printf (" {");
create_kernel (x, y);
printf (" }%s", i < SUBSAMPLE ? ",\n" : "\n");
}
printf (" }%s", j < SUBSAMPLE ? ",\n" : "\n");
}
printf ("};\n\n");
printf ("#endif /* __PIKA_BRUSH_CORE_KERNELS_H__ */\n");
return 0;
}

53
tools/meson-mkenums.sh Normal file
View File

@ -0,0 +1,53 @@
#!/bin/sh
# This is a wrapper to the tools/pika-mkenums perl script which:
# * sets a few common values;
# * updates the ${filebase}enums.c directly in the source directory in
# order for it to be versionned.
# * Create a no-op stamp-header file to be included by the resulting
# enums.c. The goal is to make sure that meson will trigger a rebuild
# of the enums.c generation before compiling, if the enums.h changed.
# See the explanation here:
# https://github.com/mesonbuild/meson/issues/10196#issuecomment-1080742592
# This is also the trick used for pdbgen.
# Arguments to this script:
# The perl binary to use.
PERL="$1"
# Root of the source directory.
top_srcdir="$2"
# Current source folder.
srcdir="$3"
# Current build folder.
builddir="$4"
# Base of the generated enums.c file name.
filebase="$5"
# Includes before #include "${filebase}enums.h"
preincludes="$6"
# Includes after #include "${filebase}enums.h"
postincludes="$7"
# Value for --dtail option if the default doesn't fit.
dtail="$8"
if [ -z "$dtail" ]; then
dtail=" { 0, NULL, NULL }\n };\n\n static GType type = 0;\n\n if (G_UNLIKELY (! type))\n {\n type = g_@type@_register_static (\"@EnumName@\", values);\n pika_type_set_translation_context (type, \"@enumnick@\");\n pika_@type@_set_value_descriptions (type, descs);\n }\n\n return type;\n}\n"
fi
$PERL $top_srcdir/tools/pika-mkenums \
--fhead "#include \"stamp-${filebase}enums.h\"\n#include \"config.h\"\n$preincludes#include \"${filebase}enums.h\"\n$postincludes" \
--fprod "\n/* enumerations from \"@basename@\" */" \
--vhead "GType\n@enum_name@_get_type (void)\n{\n static const G@Type@Value values[] =\n {" \
--vprod " { @VALUENAME@, \"@VALUENAME@\", \"@valuenick@\" }," \
--vtail " { 0, NULL, NULL }\n };\n" \
--dhead " static const Pika@Type@Desc descs[] =\n {" \
--dprod " { @VALUENAME@, @valuedesc@, @valuehelp@ },@if ('@valueabbrev@' ne 'NULL')@\n /* Translators: this is an abbreviated version of @valueudesc@.\n Keep it short. */\n { @VALUENAME@, @valueabbrev@, NULL },@endif@" \
--dtail "$dtail" \
"$srcdir/${filebase}enums.h" > "$builddir/${filebase}enums-tmp.c"
if ! cmp -s "$builddir/${filebase}enums-tmp.c" "$srcdir/${filebase}enums.c"; then
cp "$builddir/${filebase}enums-tmp.c" "$srcdir/${filebase}enums.c";
else
touch "$srcdir/${filebase}enums.c"; 2> /dev/null || true;
fi
echo "/* Generated on `date`. */" > $builddir/stamp-${filebase}enums.h

62
tools/meson.build Normal file
View File

@ -0,0 +1,62 @@
if platform_windows
pika_debug_resume = executable('pika-debug-resume',
'pika-debug-resume.c',
)
endif
pikatool = executable('pikatool' + exec_ver,
'pikatool.c',
include_directories: rootInclude,
dependencies: [
gtk3,
],
link_with: [
libpikabase,
],
c_args: [
'-DDATADIR="@0@"'.format(get_option('datadir')),
],
install: true,
)
pika_test_clipboard = executable('pika-test-clipboard' + exec_ver,
'pika-test-clipboard.c',
include_directories: rootInclude,
dependencies: [
gtk3,
],
install: true,
)
if enable_default_bin and meson.version().version_compare('>=0.61.0')
install_symlink(fs.name(pikatool.full_path()).replace(exec_ver, ''),
pointing_to: fs.name(pikatool.full_path()),
install_dir: get_option('bindir')
)
install_symlink(fs.name(pika_test_clipboard.full_path()).replace(exec_ver, ''),
pointing_to: fs.name(pika_test_clipboard.full_path()),
install_dir: get_option('bindir')
)
endif
executable('kernelgen',
'kernelgen.c',
include_directories: rootInclude,
install: false,
)
colorsvg2png = executable('colorsvg2png',
'colorsvg2png.c',
native: true,
dependencies: [
native_glib,
native_rsvg
],
# In case b_sanitize was set, we don't really care if the tool has issues (in
# particular we experienced some memory leaks with b_sanitize=address, within
# librsvg and we don't want this to break the build).
override_options: [ 'b_sanitize=none' ],
install: false,
)

View File

@ -0,0 +1,99 @@
#!/usr/bin/env python3
import sys, os
from pathlib import Path
import argparse
import json
import shutil
class Singleton(type):
_instances = {}
def __call__(cls, *args, **kwargs):
if cls not in cls._instances:
cls._instances[cls] = super(Singleton, cls).__call__(*args, **kwargs)
return cls._instances[cls]
def copytree(src, dst, symlinks=False, ignore=None):
for item in os.listdir(src):
s = os.path.join(src, item)
d = os.path.join(dst, item)
if os.path.isdir(s):
shutil.copytree(s, d, symlinks, ignore)
else:
shutil.copy2(s, d)
class MesonStatus(metaclass = Singleton):
def __init__(self):
self.get_build_dir()
self.get_meson_info()
self.get_meson_installed()
def get_build_dir(self):
self.buildroot = None
# Set up by meson.
cwd = Path(os.environ['MESON_BUILD_ROOT'])
if (cwd / 'meson-info').exists():
self.buildroot = cwd
if self.buildroot is None:
print('Error: build directory was not detected. Are you in it ?')
def get_meson_info(self):
with open(self.buildroot / 'meson-info' / 'meson-info.json') as file:
self.meson_info = json.load(file)
self.sourceroot = Path(self.meson_info['directories']['source'])
def get_meson_installed(self):
info = self.meson_info['directories']['info']
inst = self.meson_info['introspection']['information']['installed']['file']
with open(Path(info) / inst) as file:
self.installed_files = json.load(file)
def get_files_of_part(part_name):
files_of_part = {}
sourcedir = str(MesonStatus().sourceroot / part_name)
builddir = str(MesonStatus().buildroot / part_name)
for file, target in MesonStatus().installed_files.items():
if file.startswith(sourcedir + '/') or file.startswith(builddir + '/'):
files_of_part[file] = target
return files_of_part
def install_files(files, verbose):
warnings = []
for file in sorted(files.keys()):
target = files[file]
if verbose: print(file + '' + target, end='\n')
if os.path.isdir(file):
copytree(file, target)
if os.path.isfile(file):
try:
shutil.copy2(file, target)
except Exception as e:
warnings += [(file, e)]
if len(warnings) > 0:
sys.stderr.write("\n*** WARNING: *** Some file installation failed:\n")
for (file, e) in warnings:
sys.stderr.write("- {}: {}\n".format(file, e))
if __name__ == '__main__':
parser = argparse.ArgumentParser()
parser.add_argument('subdirs', nargs='+')
parser.add_argument('--verbose', '-v', action='store_true')
args = parser.parse_args()
verbose = args.verbose
for subdir in args.subdirs:
files = get_files_of_part(subdir)
if len(files) == 0:
print('Error: subdir %s does not contain any installable file' % subdir)
install_files(files, verbose)

96
tools/mnemonic-clashes Normal file
View File

@ -0,0 +1,96 @@
#!/bin/sh
srcdir="`dirname \"$0\"`/.."
find_actions () {
for f in "$srcdir"/app/actions/*-actions.c; do
< $f \
tr '\n' ' ' | \
grep -Po '{ *".*?".*?(NC_\("/*?" *, *)?".*?".*?}' | \
perl -pe 's/{ *(".*?").*?(?:NC_\(".*?" *, *)?(".*?").*?}/\1: \2,/g'
done
}
python3 - $@ <<END
from sys import argv
from itertools import chain
from glob import glob
from xml.etree.ElementTree import ElementTree
actions = {`find_actions`}
found_clashes = False
for file in glob ("$srcdir/menus/*.xml*"):
tree = ElementTree (file = file)
parents = {c: p for p in tree.iter () for c in p}
def menu_path (menu):
path = ""
while menu:
if menu.tag == "menu":
if path:
path = " -> " + path
path = menu.get ("name") + path
menu = parents.get (menu)
return path
for menu in chain (tree.iter ("menubar-and-popup"),
tree.iter ("menu"),
tree.iter ("popup")):
if len (argv) > 1 and menu.tag != argv[1]:
continue
mnemonics = {}
found_clashes_in_menu = False
for element in menu:
action = element.get ("action")
if action in actions:
label = actions[action]
if "_" in label:
mnemonic = label[label.find ("_") + 1].upper ()
if mnemonic not in mnemonics:
mnemonics[mnemonic] = []
mnemonics[mnemonic] += [action]
mnemonic_list = list (mnemonics.keys ())
mnemonic_list.sort ()
for mnemonic in mnemonic_list:
action_list = mnemonics[mnemonic]
if len (action_list) > 1:
if found_clashes:
print ()
if not found_clashes_in_menu:
if menu.tag == "menu":
print ("In %s (%s):" % (menu.get ("action"),
menu_path (menu)))
elif menu.tag == "popup":
print ("In %s:" % menu.get ("action"))
else:
print ("In top-level menu bar:")
found_clashes = True
found_clashes_in_menu = True
print (" Mnemonic clash for '%s':" % mnemonic)
for action in action_list:
print (" %s: %s" % (action, actions[action]))
exit (found_clashes)
END

View File

@ -0,0 +1,228 @@
#!/usr/bin/env python2
"""
module-dependencies.py -- PIKA library and core module dependency constructor
Copyright (C) 2010 Martin Nordholts <martinn@src.gnome.org>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
This program uses graphviz (you need PyGraphViz) to construct a graph
with dependencies between PIKA library and core modules. Run it from
the source root. Note that you'll either need the very latest
PyGraphViz binding or use this hack in agraph.py:
--- agraph.py.orig 2010-01-04 16:07:46.000000000 +0100
+++ agraph.py 2010-01-04 16:13:54.000000000 +0100
@@ -1154,7 +1154,8 @@ class AGraph(object):
raise IOError("".join(errors))
if len(errors)>0:
- raise IOError("".join(errors))
+ # Workaround exception throwing due to warning about cycles
+ pass
return "".join(data)
"""
from __future__ import with_statement
from sets import Set
import os, re, pygraphviz
# First make a sanity check
if not os.path.exists("app") or not os.path.exists("libpika"):
print("Must be run in source root!")
exit(-1);
# This file lives in libpika and is used by many low-level
# libs. Exclude it in the calculations so the graph become nicer
ignored_interface_files = [
"libpika/libpika-intl.h",
]
# List of library modules
libmodules = [
"libpika",
"libpikabase",
"libpikacolor",
"libpikaconfig",
"libpikamath",
"libpikamodule",
"libpikathumb",
"libpikawidgets",
]
# List of app modules
# XXX: Maybe group some of these together to simplify graph?
appmodules = [
"actions",
"base",
"composite",
"config",
"core",
"dialogs",
"display",
"file",
"gegl",
"gui",
"menus",
"paint",
"paint-funcs",
"pdb",
"plug-in",
"tests",
"text",
"tools",
"vectors",
"widgets",
"xcf",
]
# Bootstrap modules, i.e. modules we assume exist even though we don't
# have the code for them
boostrap_modules = [
[ "GLib", ["glib.h"] ],
[ "GTK+", ["gtk/gtk.h"] ],
[ "GEGL", ["gegl.h"] ],
[ "Pango", ["pango/pango.h"] ],
[ "Cairo", ["cairo.h"] ],
]
##
# Function to determine if a filename is for an interface file
def is_interface_file(filename):
return re.search("\.h$", filename)
##
# Function to determine if a filename is for an implementation file,
# i.e. a file that contains references to interface files
def is_implementation_file(filename):
return re.search("\.c$", filename)
##
# Represents a software module. Think of it as a node in the
# dependency graph
class Module:
def __init__(self, name, color, interface_files=[]):
self.name = name
self.color = color
self.interface_files = Set(interface_files.__iter__())
self.interface_file_dependencies = Set()
self.dependencies = Set()
def __repr__(self):
return self.name
def get_color(self):
return self.color
def get_interface_files(self):
return self.interface_files
def get_interface_file_dependencies(self):
return self.interface_file_dependencies
def get_dependencies(self):
return self.dependencies
def add_module_dependency(self, module):
if self != module:
self.dependencies.add(module)
# Represents a software module constructed from actual source code
class CodeModule(Module):
def __init__(self, path, color):
Module.__init__(self, path, color)
all_files = os.listdir(path)
# Collect interfaces this module provides
for interface_file in filter(is_interface_file, all_files):
self.interface_files.add(os.path.join(path, interface_file))
# Collect dependencies to interfaces
for filename in filter(is_implementation_file, all_files):
with open(os.path.join(path, filename), 'r') as f:
for line in f:
m = re.search("#include +[\"<](.*)[\">]", line)
if m:
interface_file = m.group(1)
# If the interface file appears to come from a core
# module, prepend with "app/"
m = re.search ("(.*)/.*", interface_file)
if m:
dirname = m.group(1)
if appmodules.__contains__(dirname):
interface_file = "app/" + interface_file
self.interface_file_dependencies.add(interface_file)
for ignored_interface_file in ignored_interface_files:
self.interface_file_dependencies.discard(ignored_interface_file)
# Initialize the modules to use for the dependency analysis
modules = Set()
for bootstrap_module in boostrap_modules:
modules.add(Module(bootstrap_module[0], 'lightblue', bootstrap_module[1]))
for module_path in libmodules:
modules.add(CodeModule(module_path, 'coral1'))
for module_path in appmodules:
modules.add(CodeModule("app/" + module_path, 'lawngreen'))
# Map the interface files in the modules to the module that hosts them
interface_file_to_module = {}
for module in modules:
for interface_file in module.get_interface_files():
interface_file_to_module[interface_file] = module
# Figure out dependencies between modules
unknown_interface_files = Set()
for module in modules:
interface_files = filter (is_interface_file, module.get_interface_file_dependencies())
for interface_file in interface_files:
if interface_file_to_module.has_key(interface_file):
module.add_module_dependency(interface_file_to_module[interface_file])
else:
unknown_interface_files.add(interface_file)
if False:
print "Unknown interface files:", unknown_interface_files
# Construct a GraphViz graph from the modules
dependency_graph = pygraphviz.AGraph(directed=True)
for module in modules:
dependency_graph.add_node(module, fillcolor=module.get_color(), style='filled')
for module in modules:
for depends_on in module.get_dependencies():
dependency_graph.add_edge(module, depends_on)
# If module A depends on module B, and module B depends on module C, A
# gets C implicitly. Perform a transitive reduction on the graph to
# reflect this
dependency_graph.tred()
# Write result
if True:
dependency_graph.draw("devel-docs/pika-module-dependencies.svg", prog="dot")
else:
dependency_graph.write("devel-docs/pika-module-dependencies.dot")

View File

@ -0,0 +1,49 @@
#!/usr/bin/env python3
"""
performance-log-close-tags.py -- Closes open tags in PIKA performance logs
Copyright (C) 2020 Ell
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
Usage: performance-log-close-tags.py < infile > outfile
"""
from xml.etree import ElementTree
import sys
class OpenTagsTracker:
open_tags = []
def start (self, tag, attrib):
self.open_tags.append (tag)
def end (self, tag):
self.open_tags.pop ()
# Read performance log from STDIN
text = sys.stdin.buffer.read ()
# Write performance log to STDOUT
sys.stdout.buffer.write (text)
# Track open tags
tracker = OpenTagsTracker ()
ElementTree.XMLParser (target = tracker).feed (text)
# Close remaining open tags
for tag in reversed (tracker.open_tags):
print ("</%s>" % tag)

View File

@ -0,0 +1,62 @@
#!/usr/bin/env python3
"""
performance-log-coalesce.py -- Coalesce PIKA performance log symbols, by
filling-in missing base addresses
Copyright (C) 2018 Ell
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
Usage: performance-log-coalesce.py < infile > outfile
"""
from xml.etree import ElementTree
import sys
empty_element = ElementTree.Element ("")
# Read performance log from STDIN
log = ElementTree.fromstring (sys.stdin.buffer.read ())
address_map = log.find ("address-map")
if address_map:
addresses = []
# Create base addresses dictionary
base_addresses = {}
for address in address_map.iterfind ("address"):
symbol = address.find ("symbol").text
source = address.find ("source").text
base = address.find ("base").text
if symbol and source and not base:
key = (symbol, source)
value = int (address.get ("value"), 0)
base_addresses[key] = min (value, base_addresses.get (key, value))
addresses.append (address)
# Fill-in missing base addresses
for address in addresses:
symbol = address.find ("symbol").text
source = address.find ("source").text
address.find ("base").text = hex (base_addresses[(symbol, source)])
# Write performance log to STDOUT
sys.stdout.buffer.write (ElementTree.tostring (log))

View File

@ -0,0 +1,91 @@
#!/usr/bin/env python3
"""
performance-log-deduce.py -- Deduce PIKA performance log thread state
Copyright (C) 2018 Ell
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
Usage: performance-log-deduce.py < infile > outfile
"""
DEDUCE_MIN_N_OCCURRENCES = 10
DEDUCE_MIN_PERCENTAGE = 0.75
from xml.etree import ElementTree
import sys
empty_element = ElementTree.Element ("")
# Read performance log from STDIN
log = ElementTree.fromstring (sys.stdin.buffer.read ())
# Construct state histogram
address_states = {}
for sample in (log.find ("samples") or empty_element).iterfind ("sample"):
threads = (sample.find ("backtrace") or empty_element).iterfind ("thread")
for thread in threads:
running = int (thread.get ("running"))
frame = thread.find ("frame")
if frame is not None:
address = frame.get ("address")
states = address_states.setdefault (address, [0, 0])
states[running] += 1
# Find maximal states
for address, states in list (address_states.items ()):
n = sum (states)
if n >= DEDUCE_MIN_N_OCCURRENCES:
state = 0
m = states[0]
for i in range (1, len (states)):
if states[i] > m:
state = i
m = states[i]
percentage = m / n
if percentage >= DEDUCE_MIN_PERCENTAGE:
address_states[address] = state
else:
del address_states[address]
else:
del address_states[address]
# Replace thread states
for sample in (log.find ("samples") or empty_element).iterfind ("sample"):
threads = (sample.find ("backtrace") or empty_element).iterfind ("thread")
for thread in threads:
frame = thread.find ("frame")
if frame is not None:
address = frame.get ("address")
running = address_states.get (address, None)
if running is not None:
thread.set ("running", str (running))
# Write performance log to STDOUT
sys.stdout.buffer.write (ElementTree.tostring (log))

View File

@ -0,0 +1,107 @@
#!/usr/bin/env python3
"""
performance-log-expand.py -- Delta-decodes PIKA performance logs
Copyright (C) 2018 Ell
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
Usage: performance-log-expand.py < infile > outfile
"""
from xml.etree import ElementTree
import sys
empty_element = ElementTree.Element ("")
# Read performance log from STDIN
log = ElementTree.fromstring (sys.stdin.buffer.read ())
try:
has_backtrace = bool (int (log.find ("params").find ("backtrace").text))
except:
has_backtrace = False
def expand_simple (element, last_values):
last_values.update ({value.tag: value.text for value in element})
for value in list (element):
element.remove (value)
for tag, text in last_values.items ():
value = ElementTree.SubElement (element, tag)
value.text = text
value.tail = "\n"
# Expand samples
last_vars = {}
last_backtrace = {}
for sample in (log.find ("samples") or empty_element).iterfind ("sample"):
# Expand variables
vars = sample.find ("vars") or \
ElementTree.SubElement (sample, "vars")
expand_simple (vars, last_vars)
# Expand backtrace
if has_backtrace:
backtrace = sample.find ("backtrace") or \
ElementTree.SubElement (sample, "backtrace")
for thread in backtrace:
id = thread.get ("id")
head = thread.get ("head")
tail = thread.get ("tail")
attrib = dict (thread.attrib)
frames = list (thread)
last_thread = last_backtrace.setdefault (id, [{}, []])
last_frames = last_thread[1]
if head:
frames = last_frames[:int (head)] + frames
del attrib["head"]
if tail:
frames = frames + last_frames[-int (tail):]
del attrib["tail"]
last_thread[0] = attrib
last_thread[1] = frames
if not frames and thread.text is None:
del last_backtrace[id]
for thread in list (backtrace):
backtrace.remove (thread)
for id, (attrib, frames) in last_backtrace.items ():
thread = ElementTree.SubElement (backtrace, "thread", attrib)
thread.text = "\n"
thread.tail = "\n"
thread.extend (frames)
# Expand address map
last_address = {}
for address in (log.find ("address-map") or empty_element).iterfind ("address"):
expand_simple (address, last_address)
# Write performance log to STDOUT
sys.stdout.buffer.write (ElementTree.tostring (log))

View File

@ -0,0 +1,54 @@
#!/usr/bin/env python3
"""
performance-log-progressive-coalesce.py -- Coalesce address-maps in progressive
PIKA performance logs
Copyright (C) 2020 Ell
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
Usage: performance-log-progressive-coalesce.py < infile > outfile
"""
from xml.etree import ElementTree
import sys
empty_element = ElementTree.Element ("")
# Read performance log from STDIN
log = ElementTree.fromstring (sys.stdin.buffer.read ())
samples = log.find ("samples") or empty_element
address_map = log.find ("address-map")
if not address_map:
address_map = ElementTree.Element ("address-map")
# Coalesce partial address maps
for partial_address_map in samples.iterfind ("address-map"):
for element in partial_address_map:
address_map.append (element)
# Remove partial address maps
for partial_address_map in samples.iterfind ("address-map"):
samples.remove (partial_address_map)
# Add global address map
if not log.find ("address-map") and len (address_map) > 0:
log.append (address_map)
# Write performance log to STDOUT
sys.stdout.buffer.write (ElementTree.tostring (log))

View File

@ -0,0 +1,55 @@
#!/usr/bin/env python3
"""
performance-log-resolve.py -- Resolve PIKA performance log backtrace symbols
Copyright (C) 2018 Ell
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
Usage: performance-log-resolve.py < infile > outfile
"""
from xml.etree import ElementTree
import sys
empty_element = ElementTree.Element ("")
# Read performance log from STDIN
log = ElementTree.fromstring (sys.stdin.buffer.read ())
address_map = log.find ("address-map")
if address_map:
# Create address dictionary
addresses = {}
for address in address_map.iterfind ("address"):
addresses[address.get ("value")] = list (address)
# Resolve addresses in backtraces
for sample in (log.find ("samples") or empty_element).iterfind ("sample"):
for thread in sample.find ("backtrace") or ():
for frame in thread:
address = addresses.get (frame.get ("address"))
if address:
frame.text = "\n"
frame.extend (address)
# Remove address map
log.remove (address_map)
# Write performance log to STDOUT
sys.stdout.buffer.write (ElementTree.tostring (log))

View File

@ -0,0 +1,39 @@
#!/bin/sh
# performance-log-viewer -- PIKA performance log viewer driver
# Copyright (C) 2018 Ell
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <https://www.gnu.org/licenses/>.
#
#
# Usage: performance-log-viewer FILE
if [ "$#" -ne 1 ]; then
echo "Usage: $0 FILE"
exit 1
fi
tools_dir="$(dirname "$(command -v -- "$0")")"
file="$1"
< "$file" || exit 1
< "$file" \
"$tools_dir/performance-log-close-tags.py" | \
"$tools_dir/performance-log-progressive-coalesce.py" | \
"$tools_dir/performance-log-expand.py" | \
"$tools_dir/performance-log-coalesce.py" | \
"$tools_dir/performance-log-deduce.py" | \
"$tools_dir/performance-log-viewer.py"

File diff suppressed because it is too large Load Diff

113
tools/pika-debug-resume.c Normal file
View File

@ -0,0 +1,113 @@
/* based on pausep by Daniel Turini
*/
#define WIN32_LEAN_AND_MEAN
#define _WIN32_WINNT 0x0502
#include <windows.h>
#include <tchar.h>
#include <tlhelp32.h>
#include <stdio.h>
#include <stdlib.h>
static BOOL
resume_process (DWORD dwOwnerPID)
{
HANDLE hThreadSnap = NULL;
BOOL bRet = FALSE;
THREADENTRY32 te32 = { 0 };
hThreadSnap = CreateToolhelp32Snapshot (TH32CS_SNAPTHREAD, 0);
if (hThreadSnap == INVALID_HANDLE_VALUE)
return FALSE;
te32.dwSize = sizeof (THREADENTRY32);
if (Thread32First (hThreadSnap, &te32))
{
do
{
if (te32.th32OwnerProcessID == dwOwnerPID)
{
HANDLE hThread = OpenThread (THREAD_SUSPEND_RESUME, FALSE, te32.th32ThreadID);
printf ("Resuming Thread: %u\n", (unsigned int) te32.th32ThreadID);
ResumeThread (hThread);
CloseHandle (hThread);
}
}
while (Thread32Next (hThreadSnap, &te32));
bRet = TRUE;
}
else
bRet = FALSE;
CloseHandle (hThreadSnap);
return bRet;
}
static BOOL
process_list (void)
{
HANDLE hProcessSnap = NULL;
BOOL bRet = FALSE;
PROCESSENTRY32 pe32 = {0};
hProcessSnap = CreateToolhelp32Snapshot (TH32CS_SNAPPROCESS, 0);
if (hProcessSnap == INVALID_HANDLE_VALUE)
return FALSE;
pe32.dwSize = sizeof (PROCESSENTRY32);
if (Process32First (hProcessSnap, &pe32))
{
do
{
printf ("PID:\t%u\t%s\n",
(unsigned int) pe32.th32ProcessID,
pe32.szExeFile);
}
while (Process32Next (hProcessSnap, &pe32));
bRet = TRUE;
}
else
bRet = FALSE;
CloseHandle (hProcessSnap);
return bRet;
}
int
main (int argc,
char* argv[])
{
DWORD pid;
if (argc <= 1)
{
process_list ();
}
else if (argc == 2)
{
pid = atoi (argv[1]);
if (pid == 0)
{
printf ("invalid: %lu\n", pid);
return 1;
}
else
{
printf ("process: %lu\n", pid);
resume_process (pid);
}
}
else
{
printf ("Usage:\n"
"resume : show processlist\n"
"resume PID: resuming thread\n");
}
return 0;
}

579
tools/pika-mkenums Normal file
View File

@ -0,0 +1,579 @@
#!/usr/bin/env perl
use warnings;
# This is pika-mkenums, a perl script based on glib-mkenums.
# It can be used just like glib-mkenums but offers one extra
# feature: The keyword "desc" is recognized in trigraphs and
# allows to specify a literal description of the enum value.
# This description is used to generate user interface elements
# from the enumeration. To allow i18n of the description, the
# value is by default put into the N_() macro.
use Text::ParseWords;
use File::Basename;
# pika-mkenums
# Information about the current enumeration
my $flags; # Is enumeration a bitmask?
my $option_lowercase_name; # A lower case name to use as part of the *_get_type() function, instead of the one that we guess.
# For instance, when an enum uses abnormal capitalization and we can not guess where to put the underscores.
my $seenbitshift; # Have we seen bitshift operators?
my $enum_prefix; # Prefix for this enumeration
my $enumname; # Name for this enumeration
my $enumshort; # $enumname without prefix
my $enumnick; # lower case version of $enumshort
my $enumindex = 0; # Global enum counter
my $firstenum = 1; # Is this the first enumeration per file?
my @entries; # [ $name, $val ] for each entry
sub parse_trigraph {
my $opts = shift;
my @opts;
for $opt (quotewords(",", "true", $opts)) {
$opt =~ s/^\s*//;
$opt =~ s/\s*$//;
my ($key,$val) = $opt =~ /(\w+)(?:=(.+))?/;
defined $val or $val = 1;
push @opts, $key, $val;
}
@opts;
}
sub parse_entries {
my $file = shift;
my $file_name = shift;
my $looking_for_name = 0;
while (<$file>) {
# read lines until we have no open comments
while (m@/\*([^*]|\*(?!/))*$@) {
my $new;
defined ($new = <$file>) || die "Unmatched comment in $ARGV";
$_ .= $new;
}
# strip comments w/o options
s@/\*(?!<)
([^*]+|\*(?!/))*
\*/@@gx;
# strip newlines
s@\n@ @;
# skip empty lines
next if m@^\s*$@;
if ($looking_for_name) {
if (/^\s*(\w+)/) {
$enumname = $1;
return 1;
}
}
# Handle include files
if (/^\#include\s*<([^>]*)>/ ) {
my $file= "../$1";
open NEWFILE, $file or die "Cannot open include file $file: $!\n";
if (parse_entries (\*NEWFILE, $NEWFILE)) {
return 1;
} else {
next;
}
}
if (/^\s*\}\s*(\w+)/) {
$enumname = $1;
$enumindex++;
return 1;
}
if (/^\s*\}/) {
$enumindex++;
$looking_for_name = 1;
next;
}
if (m@^\s*
(\w+)\s* # name
(?:=( # value
\s*\w+\s*\(.*\)\s* # macro with multiple args
| # OR
(?:[^,/]|/(?!\*))* # anything but a comma or comment
))?,?\s*
(?:/\*< # options
(([^*]|\*(?!/))*)
>\s*\*/)?,?
\s*$
@x) {
my ($name, $value, $options) = ($1,$2,$3);
if (!defined $flags && defined $value && $value =~ /<</) {
$seenbitshift = 1;
}
if (defined $options) {
my %options = parse_trigraph($options);
if (!defined $options{"skip"}) {
push @entries, [ $name, $options{nick}, $options{desc}, $options{help}, $options{abbrev} ];
}
} else {
push @entries, [ $name ];
}
# skip remaining lines of multiline values
do {
if (m/}/) {
redo;
} elsif (m@,\s*(/\*.*?\*/\s*)*$@) {
next;
}
} while (<$file>);
# failed to find end of value. bail
die "$0: $file_name:$.: Unterminated enum, while processing `$name'\n";
} elsif (m@^\s*\#@) {
# ignore preprocessor directives
} else {
print STDERR "$0: $file_name:$.: Failed to parse `$_'\n";
}
}
return 0;
}
sub version {
print STDERR "pika-mkenums based on glib-mkenums version glib-2.0\n";
print STDERR "pika-mkenums comes with ABSOLUTELY NO WARRANTY.\n";
print STDERR "You may redistribute copies of pika-mkenums under the terms\n";
print STDERR "of the GNU General Public License which can be found in the\n";
print STDERR "PIKA source package.";
exit 0;
}
sub usage {
print STDERR "Usage: pika-mkenums [options] [files...]\n";
print STDERR " --fhead <text> output file header\n";
print STDERR " --fprod <text> per input file production\n";
print STDERR " --ftail <text> output file trailer\n";
print STDERR " --eprod <text> per enum text (produced prior to value itarations)\n";
print STDERR " --vhead <text> value header, produced before iterating over enum values\n";
print STDERR " --vprod <text> value text, produced for each enum value\n";
print STDERR " --vtail <text> value tail, produced after iterating over enum values\n";
print STDERR " --dhead <text> description header, produced before iterating over enum value descriptions\n";
print STDERR " --dprod <text> description text, produced for each enum value description\n";
print STDERR " --dtail <text> description tail, produced after iterating over enum value descriptions\n";
print STDERR " --comments <text> comment structure\n";
print STDERR " -h, --help show this help message\n";
print STDERR " -v, --version print version information\n";
print STDERR "Production text substitutions:\n";
print STDERR " \@EnumName\@ PrefixTheXEnum\n";
print STDERR " \@enum_name\@ prefix_the_xenum\n";
print STDERR " \@ENUMNAME\@ PREFIX_THE_XENUM\n";
print STDERR " \@ENUMSHORT\@ THE_XENUM\n";
print STDERR " \@enumnick\@ the_xenum\n";
print STDERR " \@VALUENAME\@ PREFIX_THE_XVALUE\n";
print STDERR " \@valuenick\@ the-xvalue\n";
print STDERR " \@valuedesc\@ descriptions as defined in the header\n";
print STDERR " \@valuehelp\@ help texts as defined in the header\n";
print STDERR " \@valueabbrev\@ abbreviations as defined in the header\n";
print STDERR " \@valueudesc\@ untranslated descriptions as defined in the header\n";
print STDERR " \@valueuhelp\@ untranslated help texts as defined in the header\n";
print STDERR " \@valueuabbrev\@ untranslated abbreviations as defined in the header\n";
print STDERR " \@type\@ either enum or flags\n";
print STDERR " \@Type\@ either Enum or Flags\n";
print STDERR " \@TYPE\@ either ENUM or FLAGS\n";
print STDERR " \@filename\@ name of current input file\n";
print STDERR " \@basename\@ basename of current input file\n";
print STDERR " \@if (...)\@ ... \@endif\@ conditional inclusion\n";
exit 0;
}
# production variables:
my $fhead = ""; # output file header
my $fprod = ""; # per input file production
my $ftail = ""; # output file trailer
my $eprod = ""; # per enum text (produced prior to value itarations)
my $vhead = ""; # value header, produced before iterating over enum values
my $vprod = ""; # value text, produced for each enum value
my $vtail = ""; # value tail, produced after iterating over enum values
my $dhead = ""; # desc header, produced before iterating over enum values
my $dprod = ""; # desc text, produced for each enum value
my $dtail = ""; # desc tail, produced after iterating over enum values
# other options
my $comment_tmpl = "/* \@comment\@ */";
if (!defined $ARGV[0]) {
usage;
}
while ($_ = $ARGV[0], /^-/) {
shift;
last if /^--$/;
if (/^--fhead$/) { $fhead = $fhead . shift }
elsif (/^--fprod$/) { $fprod = $fprod . shift }
elsif (/^--ftail$/) { $ftail = $ftail . shift }
elsif (/^--eprod$/) { $eprod = $eprod . shift }
elsif (/^--vhead$/) { $vhead = $vhead . shift }
elsif (/^--vprod$/) { $vprod = $vprod . shift }
elsif (/^--vtail$/) { $vtail = $vtail . shift }
elsif (/^--dhead$/) { $dhead = $dhead . shift }
elsif (/^--dprod$/) { $dprod = $dprod . shift }
elsif (/^--dtail$/) { $dtail = $dtail . shift }
elsif (/^--comments$/) { $comment_tmpl = shift }
elsif (/^--help$/ || /^-h$/) { usage; }
elsif (/^--version$/ || /^-v$/) { version; }
else { usage; }
}
# put auto-generation comment
{
my $comment = $comment_tmpl;
$comment =~ s/\@comment\@/Generated data (by pika-mkenums)/;
print "\n" . $comment . "\n\n";
}
if (length($fhead)) {
my $prod = $fhead;
$prod =~ s/\@filename\@/$ARGV[0]/g;
$prod =~ s/\@basename\@/basename($ARGV[0])/ge;
$prod =~ s/\\a/\a/g; $prod =~ s/\\b/\b/g; $prod =~ s/\\t/\t/g; $prod =~ s/\\n/\n/g;
$prod =~ s/\\f/\f/g; $prod =~ s/\\r/\r/g;
print "$prod\n";
}
while (<>) {
if (eof) {
close (ARGV); # reset line numbering
$firstenum = 1; # Flag to print filename at next enum
}
# read lines until we have no open comments
while (m@/\*([^*]|\*(?!/))*$@) {
my $new;
defined ($new = <>) || die "Unmatched comment in $ARGV";
$_ .= $new;
}
# strip comments w/o options
s@/\*(?!<)
([^*]+|\*(?!/))*
\*/@@gx;
# ignore forward declarations
next if /^\s*typedef\s+enum.*;/;
if (m@^\s*typedef\s+enum\s*
(\{)?\s*
(?:/\*<
(([^*]|\*(?!/))*)
>\s*\*/)?
@x) {
if (defined $2) {
my %options = parse_trigraph ($2);
next if defined $options{"skip"};
$enum_prefix = $options{prefix};
$flags = $options{flags};
$option_lowercase_name = $options{lowercase_name};
} else {
$enum_prefix = undef;
$flags = undef;
$option_lowercase_name = undef;
}
# Didn't have trailing '{' look on next lines
if (!defined $1) {
while (<>) {
if (eof) {
die "Hit end of file while parsing enum in $ARGV";
}
if (s/^\s*\{//) {
last;
}
}
}
$seenbitshift = 0;
@entries = ();
# Now parse the entries
parse_entries (\*ARGV, $ARGV);
# figure out if this was a flags or enums enumeration
if (!defined $flags) {
$flags = $seenbitshift;
}
# Autogenerate a prefix
if (!defined $enum_prefix) {
for (@entries) {
my $nick = $_->[1];
if (!defined $nick) {
my $name = $_->[0];
if (defined $enum_prefix) {
my $tmp = ~ ($name ^ $enum_prefix);
($tmp) = $tmp =~ /(^\xff*)/;
$enum_prefix = $enum_prefix & $tmp;
} else {
$enum_prefix = $name;
}
}
}
if (!defined $enum_prefix) {
$enum_prefix = "";
} else {
# Trim so that it ends in an underscore
$enum_prefix =~ s/_[^_]*$/_/;
}
} else {
# canonicalize user defined prefixes
$enum_prefix = uc($enum_prefix);
$enum_prefix =~ s/-/_/g;
$enum_prefix =~ s/(.*)([^_])$/$1$2_/;
}
# enumname is e.g. GMatchType
$enspace = $enumname;
$enspace =~ s/^([A-Z][a-z]*).*$/$1/;
$enumshort = $enumname;
$enumshort =~ s/^[A-Z][a-z]*//;
$enumshort =~ s/([^A-Z])([A-Z])/$1_$2/g;
$enumshort =~ s/([A-Z][A-Z])([A-Z][0-9a-z])/$1_$2/g;
$enumshort = uc($enumshort);
$enumlong = uc($enspace) . "_" . $enumshort;
$enumsym = lc($enspace) . "_" . lc($enumshort);
$enumnick = lc($enumshort);
$enumnick =~ tr/_/-/;
for $entry (@entries) {
my ($name,$nick,$desc,$help,$abbrev) = @{$entry};
if (!defined $nick) {
($nick = $name) =~ s/^$enum_prefix//;
$nick =~ tr/_/-/;
$nick = lc($nick);
}
if (!defined $desc) {
$udesc = "\"$name\"";
} else {
$udesc = $desc;
}
if (!defined $desc) {
$desc = "\"$name\"";
} else {
$desc = "NC_(\"$enumnick\", $desc)";
}
if (!defined $help) {
$uhelp = "NULL";
} else {
$uhelp = $help;
}
if (!defined $help) {
$help = "NULL";
} else {
$help = "N_($help)";
}
if (!defined $abbrev) {
$uabbrev = "NULL";
} else {
$uabbrev = $abbrev;
}
if (!defined $abbrev) {
$abbrev = "NULL";
} else {
$abbrev = "NC_(\"$enumnick\", $abbrev)";
}
@{$entry} = ($name, $nick, $desc, $help, $abbrev, $udesc, $uhelp, $uabbrev);
}
# Spit out the output
# The options might override the lower case name if it could
# not be generated correctly:
if (defined($option_lowercase_name)) {
$enumsym = $option_lowercase_name;
}
if ($firstenum) {
$firstenum = 0;
if (length($fprod)) {
my $prod = $fprod;
$prod =~ s/\@filename\@/$ARGV/g;
$prod =~ s/\@basename\@/basename($ARGV)/ge;
$prod =~ s/\\a/\a/g; $prod =~ s/\\b/\b/g; $prod =~ s/\\t/\t/g; $prod =~ s/\\n/\n/g;
$prod =~ s/\\f/\f/g; $prod =~ s/\\r/\r/g;
print "$prod\n";
}
}
if (length($eprod)) {
my $prod = $eprod;
$prod =~ s/\@enum_name\@/$enumsym/g;
$prod =~ s/\@EnumName\@/$enumname/g;
$prod =~ s/\@ENUMSHORT\@/$enumshort/g;
$prod =~ s/\@enumnick\@/$enumnick/g;
$prod =~ s/\@ENUMNAME\@/$enumlong/g;
if ($flags) { $prod =~ s/\@type\@/flags/g; } else { $prod =~ s/\@type\@/enum/g; }
if ($flags) { $prod =~ s/\@Type\@/Flags/g; } else { $prod =~ s/\@Type\@/Enum/g; }
if ($flags) { $prod =~ s/\@TYPE\@/FLAGS/g; } else { $prod =~ s/\@TYPE\@/ENUM/g; }
$prod =~ s/\@if (\((?>[^()]|(?1))*\))\@(.*?)\@endif\@/eval ($1) ? "$2" : ""/ges;
$prod =~ s/\\a/\a/g; $prod =~ s/\\b/\b/g; $prod =~ s/\\t/\t/g; $prod =~ s/\\n/\n/g;
$prod =~ s/\\f/\f/g; $prod =~ s/\\r/\r/g;
print "$prod\n";
}
if (length($vhead)) {
my $prod = $vhead;
$prod =~ s/\@enum_name\@/$enumsym/g;
$prod =~ s/\@EnumName\@/$enumname/g;
$prod =~ s/\@ENUMSHORT\@/$enumshort/g;
$prod =~ s/\@enumnick\@/$enumnick/g;
$prod =~ s/\@ENUMNAME\@/$enumlong/g;
if ($flags) { $prod =~ s/\@type\@/flags/g; } else { $prod =~ s/\@type\@/enum/g; }
if ($flags) { $prod =~ s/\@Type\@/Flags/g; } else { $prod =~ s/\@Type\@/Enum/g; }
if ($flags) { $prod =~ s/\@TYPE\@/FLAGS/g; } else { $prod =~ s/\@TYPE\@/ENUM/g; }
$prod =~ s/\@if (\((?>[^()]|(?1))*\))\@(.*?)\@endif\@/eval ($1) ? "$2" : ""/ges;
$prod =~ s/\\a/\a/g; $prod =~ s/\\b/\b/g; $prod =~ s/\\t/\t/g; $prod =~ s/\\n/\n/g;
$prod =~ s/\\f/\f/g; $prod =~ s/\\r/\r/g;
print "$prod\n";
}
if (length($vprod)) {
my $prod = $vprod;
$prod =~ s/\\a/\a/g; $prod =~ s/\\b/\b/g; $prod =~ s/\\t/\t/g; $prod =~ s/\\n/\n/g;
$prod =~ s/\\f/\f/g; $prod =~ s/\\r/\r/g;
for (@entries) {
my ($name,$nick,$desc,$help,$abbrev,$udesc,$uhelp,$uabbrev) = @{$_};
my $tmp_prod = $prod;
$tmp_prod =~ s/\@VALUENAME\@/$name/g;
$tmp_prod =~ s/\@valuenick\@/$nick/g;
$tmp_prod =~ s/\@valuedesc\@/$desc/g;
$tmp_prod =~ s/\@valuehelp\@/$help/g;
$tmp_prod =~ s/\@valueabbrev\@/$abbrev/g;
$tmp_prod =~ s/\@valueudesc\@/$udesc/g;
$tmp_prod =~ s/\@valueuhelp\@/$uhelp/g;
$tmp_prod =~ s/\@valueuabbrev\@/$uabbrev/g;
if ($flags) { $tmp_prod =~ s/\@type\@/flags/g; } else { $tmp_prod =~ s/\@type\@/enum/g; }
if ($flags) { $tmp_prod =~ s/\@Type\@/Flags/g; } else { $tmp_prod =~ s/\@Type\@/Enum/g; }
if ($flags) { $tmp_prod =~ s/\@TYPE\@/FLAGS/g; } else { $tmp_prod =~ s/\@TYPE\@/ENUM/g; }
$tmp_prod =~ s/\@if (\((?>[^()]|(?1))*\))\@(.*?)\@endif\@/eval ($1) ? "$2" : ""/ges;
print "$tmp_prod\n";
}
}
if (length($vtail)) {
my $prod = $vtail;
$prod =~ s/\@enum_name\@/$enumsym/g;
$prod =~ s/\@EnumName\@/$enumname/g;
$prod =~ s/\@ENUMSHORT\@/$enumshort/g;
$prod =~ s/\@enumnick\@/$enumnick/g;
$prod =~ s/\@ENUMNAME\@/$enumlong/g;
if ($flags) { $prod =~ s/\@type\@/flags/g; } else { $prod =~ s/\@type\@/enum/g; }
if ($flags) { $prod =~ s/\@Type\@/Flags/g; } else { $prod =~ s/\@Type\@/Enum/g; }
if ($flags) { $prod =~ s/\@TYPE\@/FLAGS/g; } else { $prod =~ s/\@TYPE\@/ENUM/g; }
$prod =~ s/\@if (\((?>[^()]|(?1))*\))\@(.*?)\@endif\@/eval ($1) ? "$2" : ""/ges;
$prod =~ s/\\a/\a/g; $prod =~ s/\\b/\b/g; $prod =~ s/\\t/\t/g; $prod =~ s/\\n/\n/g;
$prod =~ s/\\f/\f/g; $prod =~ s/\\r/\r/g;
print "$prod\n";
}
if (length($dhead)) {
my $prod = $dhead;
$prod =~ s/\@enum_name\@/$enumsym/g;
$prod =~ s/\@EnumName\@/$enumname/g;
$prod =~ s/\@ENUMSHORT\@/$enumshort/g;
$prod =~ s/\@enumnick\@/$enumnick/g;
$prod =~ s/\@ENUMNAME\@/$enumlong/g;
if ($flags) { $prod =~ s/\@type\@/flags/g; } else { $prod =~ s/\@type\@/enum/g; }
if ($flags) { $prod =~ s/\@Type\@/Flags/g; } else { $prod =~ s/\@Type\@/Enum/g; }
if ($flags) { $prod =~ s/\@TYPE\@/FLAGS/g; } else { $prod =~ s/\@TYPE\@/ENUM/g; }
$prod =~ s/\@if (\((?>[^()]|(?1))*\))\@(.*?)\@endif\@/eval ($1) ? "$2" : ""/ges;
$prod =~ s/\\a/\a/g; $prod =~ s/\\b/\b/g; $prod =~ s/\\t/\t/g; $prod =~ s/\\n/\n/g;
$prod =~ s/\\f/\f/g; $prod =~ s/\\r/\r/g;
print "$prod\n";
}
if (length($dprod)) {
my $prod = $dprod;
$prod =~ s/\\a/\a/g; $prod =~ s/\\b/\b/g; $prod =~ s/\\t/\t/g; $prod =~ s/\\n/\n/g;
$prod =~ s/\\f/\f/g; $prod =~ s/\\r/\r/g;
for (@entries) {
my ($name,$nick,$desc,$help,$abbrev,$udesc,$uhelp,$uabbrev) = @{$_};
my $tmp_prod = $prod;
$tmp_prod =~ s/\@VALUENAME\@/$name/g;
$tmp_prod =~ s/\@valuenick\@/$nick/g;
$tmp_prod =~ s/\@valuedesc\@/$desc/g;
$tmp_prod =~ s/\@valuehelp\@/$help/g;
$tmp_prod =~ s/\@valueabbrev\@/$abbrev/g;
$tmp_prod =~ s/\@valueudesc\@/$udesc/g;
$tmp_prod =~ s/\@valueuhelp\@/$uhelp/g;
$tmp_prod =~ s/\@valueuabbrev\@/$uabbrev/g;
if ($flags) { $tmp_prod =~ s/\@type\@/flags/g; } else { $tmp_prod =~ s/\@type\@/enum/g; }
if ($flags) { $tmp_prod =~ s/\@Type\@/Flags/g; } else { $tmp_prod =~ s/\@Type\@/Enum/g; }
if ($flags) { $tmp_prod =~ s/\@TYPE\@/FLAGS/g; } else { $tmp_prod =~ s/\@TYPE\@/ENUM/g; }
$tmp_prod =~ s/\@if (\((?>[^()]|(?1))*\))\@(.*?)\@endif\@/eval ($1) ? "$2" : ""/ges;
print "$tmp_prod\n";
}
}
if (length($dtail)) {
my $prod = $dtail;
$prod =~ s/\@enum_name\@/$enumsym/g;
$prod =~ s/\@EnumName\@/$enumname/g;
$prod =~ s/\@ENUMSHORT\@/$enumshort/g;
$prod =~ s/\@enumnick\@/$enumnick/g;
$prod =~ s/\@ENUMNAME\@/$enumlong/g;
if ($flags) { $prod =~ s/\@type\@/flags/g; } else { $prod =~ s/\@type\@/enum/g; }
if ($flags) { $prod =~ s/\@Type\@/Flags/g; } else { $prod =~ s/\@Type\@/Enum/g; }
if ($flags) { $prod =~ s/\@TYPE\@/FLAGS/g; } else { $prod =~ s/\@TYPE\@/ENUM/g; }
$prod =~ s/\@if (\((?>[^()]|(?1))*\))\@(.*?)\@endif\@/eval ($1) ? "$2" : ""/ges;
$prod =~ s/\\a/\a/g; $prod =~ s/\\b/\b/g; $prod =~ s/\\t/\t/g; $prod =~ s/\\n/\n/g;
$prod =~ s/\\f/\f/g; $prod =~ s/\\r/\r/g;
print "$prod\n";
}
}
}
if (length($ftail)) {
my $prod = $ftail;
$prod =~ s/\@filename\@/$ARGV/g;
$prod =~ s/\@basename\@/basename($ARGV)/ge;
$prod =~ s/\@if (\((?>[^()]|(?1))*\))\@(.*?)\@endif\@/eval ($1) ? "$2" : ""/ges;
$prod =~ s/\\a/\a/g; $prod =~ s/\\b/\b/g; $prod =~ s/\\t/\t/g; $prod =~ s/\\n/\n/g;
$prod =~ s/\\f/\f/g; $prod =~ s/\\r/\r/g;
print "$prod\n";
}
# put auto-generation comment
{
my $comment = $comment_tmpl;
$comment =~ s/\@comment\@/Generated data ends here/;
$comment =~ s/\@if (\((?>[^()]|(?1))*\))\@(.*?)\@endif\@/eval ($1) ? "$2" : ""/ges;
print "\n" . $comment . "\n\n";
}

443
tools/pika-test-clipboard.c Normal file
View File

@ -0,0 +1,443 @@
/*
* pika-test-clipboard.c -- do clipboard things
*
* Copyright (C) 2005 Michael Natterer <mitch@gimp.org>
*
* Use this code for whatever you like.
*/
#include "config.h"
#include <errno.h>
#include <fcntl.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif
#include <glib/gstdio.h>
#ifndef _O_BINARY
#define _O_BINARY 0
#endif
#include <gtk/gtk.h>
#include "libpikabase/pikabase.h"
typedef struct _CopyData CopyData;
struct _CopyData
{
const gchar *filename;
gboolean file_copied;
GError *error;
};
static void test_clipboard_show_version (void) G_GNUC_NORETURN;
static gboolean test_clipboard_parse_selection (const gchar *option_name,
const gchar *value,
gpointer data,
GError **error);
static gboolean test_clipboard_list_targets (GtkClipboard *clipboard);
static gboolean test_clipboard_copy (GtkClipboard *clipboard,
const gchar *target,
const gchar *filename);
static gboolean test_clipboard_store (GtkClipboard *clipboard,
const gchar *target,
const gchar *filename);
static gboolean test_clipboard_paste (GtkClipboard *clipboard,
const gchar *target,
const gchar *filename);
static void test_clipboard_copy_callback (GtkClipboard *clipboard,
GtkSelectionData *selection,
guint info,
gpointer data);
static GdkAtom option_selection_type = GDK_SELECTION_CLIPBOARD;
static gboolean option_list_targets = FALSE;
static gchar *option_target = NULL;
static gchar *option_copy_filename = NULL;
static gchar *option_store_filename = NULL;
static gchar *option_paste_filename = NULL;
static const GOptionEntry main_entries[] =
{
{
"selection-type", 's', 0,
G_OPTION_ARG_CALLBACK, test_clipboard_parse_selection,
"Selection type (primary|secondary|clipboard)", "<type>"
},
{
"list-targets", 'l', 0,
G_OPTION_ARG_NONE, &option_list_targets,
"List the targets offered by the clipboard", NULL
},
{
"target", 't', 0,
G_OPTION_ARG_STRING, &option_target,
"The target format to copy or paste", "<target>"
},
{
"copy", 'c', 0,
G_OPTION_ARG_STRING, &option_copy_filename,
"Copy <file> to clipboard", "<file>"
},
{
"store", 'S', 0,
G_OPTION_ARG_STRING, &option_store_filename,
"Store <file> in the clipboard manager", "<file>"
},
{
"paste", 'p', 0,
G_OPTION_ARG_STRING, &option_paste_filename,
"Paste clipboard into <file> ('-' pastes to STDOUT)", "<file>"
},
{
"version", 'v', G_OPTION_FLAG_NO_ARG,
G_OPTION_ARG_CALLBACK, test_clipboard_show_version,
"Show version information and exit", NULL
},
{ NULL }
};
gint
main (gint argc,
gchar *argv[])
{
GOptionContext *context;
GtkClipboard *clipboard;
GError *error = NULL;
context = g_option_context_new (NULL);
g_option_context_add_main_entries (context, main_entries, NULL);
g_option_context_add_group (context, gtk_get_option_group (TRUE));
if (! g_option_context_parse (context, &argc, &argv, &error))
{
if (error)
{
g_printerr ("%s\n", error->message);
g_error_free (error);
}
else
{
g_print ("%s\n",
"Could not initialize the graphical user interface.\n"
"Make sure a proper setup for your display environment "
"exists.");
}
return EXIT_FAILURE;
}
gtk_init (&argc, &argv);
clipboard = gtk_clipboard_get_for_display (gdk_display_get_default (),
option_selection_type);
if (! clipboard)
g_error ("gtk_clipboard_get_for_display");
if (option_list_targets)
{
if (! test_clipboard_list_targets (clipboard))
return EXIT_FAILURE;
return EXIT_SUCCESS;
}
if ((option_copy_filename && option_paste_filename) ||
(option_copy_filename && option_store_filename) ||
(option_paste_filename && option_store_filename))
{
g_printerr ("Can't perform two operations at the same time\n");
return EXIT_FAILURE;
}
if (option_copy_filename)
{
if (! option_target)
{
g_printerr ("Usage: %s -t <target> -c <file>\n", argv[0]);
return EXIT_FAILURE;
}
if (! test_clipboard_copy (clipboard, option_target,
option_copy_filename))
return EXIT_FAILURE;
}
if (option_store_filename)
{
if (! option_target)
{
g_printerr ("Usage: %s -t <target> -S <file>\n", argv[0]);
return EXIT_FAILURE;
}
if (! test_clipboard_store (clipboard, option_target,
option_store_filename))
return EXIT_FAILURE;
}
if (option_paste_filename)
{
if (! option_target)
{
g_printerr ("Usage: %s -t <target> -p <file>\n", argv[0]);
return EXIT_FAILURE;
}
if (! test_clipboard_paste (clipboard, option_target,
option_paste_filename))
return EXIT_FAILURE;
}
return EXIT_SUCCESS;
}
static void
test_clipboard_show_version (void)
{
g_print ("pika-test-clipboard (PIKA clipboard testbed) version %s\n",
PIKA_VERSION);
exit (EXIT_SUCCESS);
}
static gboolean
test_clipboard_parse_selection (const gchar *option_name,
const gchar *value,
gpointer data,
GError **error)
{
if (! strcmp (value, "primary"))
option_selection_type = GDK_SELECTION_PRIMARY;
else if (! strcmp (value, "secondary"))
option_selection_type = GDK_SELECTION_SECONDARY;
else if (! strcmp (value, "clipboard"))
option_selection_type = GDK_SELECTION_CLIPBOARD;
else
return FALSE;
return TRUE;
}
static gboolean
test_clipboard_list_targets (GtkClipboard *clipboard)
{
GtkSelectionData *data;
data = gtk_clipboard_wait_for_contents (clipboard,
gdk_atom_intern ("TARGETS",
FALSE));
if (data)
{
GdkAtom *targets;
gint n_targets;
gboolean success;
success = gtk_selection_data_get_targets (data, &targets, &n_targets);
gtk_selection_data_free (data);
if (success)
{
gint i;
for (i = 0; i < n_targets; i++)
g_print ("%s\n", gdk_atom_name (targets[i]));
g_free (targets);
}
}
return TRUE;
}
static gboolean
test_clipboard_copy (GtkClipboard *clipboard,
const gchar *target,
const gchar *filename)
{
GtkTargetEntry entry;
CopyData data;
entry.target = g_strdup (target);
entry.flags = 0;
entry.info = 1;
data.filename = filename;
data.file_copied = FALSE;
data.error = NULL;
if (! gtk_clipboard_set_with_data (clipboard, &entry, 1,
test_clipboard_copy_callback,
NULL,
&data))
{
g_printerr ("%s: gtk_clipboard_set_with_data() failed\n",
g_get_prgname());
return FALSE;
}
gtk_main ();
if (! data.file_copied)
{
if (data.error)
{
g_printerr ("%s: copying failed: %s\n",
g_get_prgname (), data.error->message);
g_error_free (data.error);
}
else
{
g_printerr ("%s: copying failed\n",
g_get_prgname ());
}
return FALSE;
}
return TRUE;
}
static gboolean
test_clipboard_store (GtkClipboard *clipboard,
const gchar *target,
const gchar *filename)
{
GtkTargetEntry entry;
CopyData data;
entry.target = g_strdup (target);
entry.flags = 0;
entry.info = 1;
data.filename = filename;
data.file_copied = FALSE;
data.error = NULL;
if (! gtk_clipboard_set_with_data (clipboard, &entry, 1,
test_clipboard_copy_callback,
NULL,
&data))
{
g_printerr ("%s: gtk_clipboard_set_with_data() failed\n",
g_get_prgname ());
return FALSE;
}
gtk_clipboard_set_can_store (clipboard, &entry, 1);
gtk_clipboard_store (clipboard);
if (! data.file_copied)
{
if (data.error)
{
g_printerr ("%s: storing failed: %s\n",
g_get_prgname (), data.error->message);
g_error_free (data.error);
}
else
{
g_printerr ("%s: could not contact clipboard manager\n",
g_get_prgname ());
}
return FALSE;
}
return TRUE;
}
static gboolean
test_clipboard_paste (GtkClipboard *clipboard,
const gchar *target,
const gchar *filename)
{
GtkSelectionData *sel_data;
sel_data = gtk_clipboard_wait_for_contents (clipboard,
gdk_atom_intern (target,
FALSE));
if (sel_data)
{
const guchar *data;
gint length;
gint fd;
if (! strcmp (filename, "-"))
fd = 1;
else
fd = g_open (filename, O_WRONLY | O_CREAT | O_TRUNC | _O_BINARY, 0666);
if (fd < 0)
{
g_printerr ("%s: open() filed: %s",
g_get_prgname (), g_strerror (errno));
return FALSE;
}
data = gtk_selection_data_get_data (sel_data);
length = gtk_selection_data_get_length (sel_data);
if (write (fd, data, length) < length)
{
close (fd);
g_printerr ("%s: write() failed: %s",
g_get_prgname (), g_strerror (errno));
return FALSE;
}
if (close (fd) < 0)
{
g_printerr ("%s: close() failed: %s",
g_get_prgname (), g_strerror (errno));
return FALSE;
}
gtk_selection_data_free (sel_data);
}
return TRUE;
}
static void
test_clipboard_copy_callback (GtkClipboard *clipboard,
GtkSelectionData *selection,
guint info,
gpointer data)
{
CopyData *copy_data = data;
gchar *buf;
gsize buf_size;
if (! g_file_get_contents (copy_data->filename, &buf, &buf_size,
&copy_data->error))
{
if (! option_store_filename)
gtk_main_quit ();
return;
}
gtk_selection_data_set (selection,
gtk_selection_data_get_target (selection),
8, (guchar *) buf, buf_size);
g_free (buf);
copy_data->file_copied = TRUE;
g_print ("%s: data transfer in progress, hit <ctrl>+c when pasted...",
G_STRFUNC);
}

117
tools/pikapath2svg.py Normal file
View File

@ -0,0 +1,117 @@
#!/usr/bin/env python2
import sys,re
"""
pikapath2svg.py -- Converts Gimp-Paths to a SVG-File.
Copyright (C) 2000 Simon Budig <simon@gimp.org>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
Usage: pikapath2svg.py [infile [outfile]]
"""
svgtemplate = """<?xml version="1.0" standalone="yes"?>
<svg width="%d" height="%d">
<g>
<path id="%s" transform="translate (%d,%d)"
style="stroke:#000000; stroke-width:1; fill:none"
d="%s"/>
</g>
</svg>
"""
emptysvgtemplate = """<?xml version="1.0" standalone="yes"?>
<svg width="1" height="1">
</svg>
"""
class Path:
def __init__(self):
self.name = ""
self.svgpath = ""
self.pikapoints = [[]]
self.bounds = None
def readpikafile (self, filedesc):
text = filedesc.readlines()
for line in text:
namematch = re.match ("Name: (.*)$", line)
if namematch:
path.name = namematch.group(1)
pointmatch = re.match ("TYPE: (\d) X: (\d+) Y: (\d+)", line)
if pointmatch:
if pointmatch.group (1) == "3":
path.pikapoints.append ([])
(x, y) = map (int, pointmatch.groups()[1:])
path.pikapoints[-1].append (map (int, pointmatch.groups()))
if self.bounds:
if self.bounds[0] > x: self.bounds[0] = x
if self.bounds[1] > y: self.bounds[1] = y
if self.bounds[2] < x: self.bounds[2] = x
if self.bounds[3] < y: self.bounds[3] = y
else:
self.bounds = [x,y,x,y]
def makesvg (self):
for path in self.pikapoints:
if path:
start = path[0]
svg = "M %d %d " % tuple (start[1:])
path = path[1:]
while path:
curve = path [0:3]
path = path[3:]
if len (curve) == 2:
svg = svg + "C %d %d %d %d %d %d z " % tuple (
tuple (curve [0][1:]) +
tuple (curve [1][1:]) +
tuple (start [1:]))
if len (curve) == 3:
svg = svg + "C %d %d %d %d %d %d " % tuple (
tuple (curve [0][1:]) +
tuple (curve [1][1:]) +
tuple (curve [2][1:]))
self.svgpath = self.svgpath + svg
def writesvgfile (self, outfile):
if self.svgpath:
svg = svgtemplate % (self.bounds[2]-self.bounds[0],
self.bounds[3]-self.bounds[1],
self.name,
-self.bounds[0], -self.bounds[1],
self.svgpath)
else:
svg = emptysvgtemplate
outfile.write (svg)
if len (sys.argv) > 1:
infile = open (sys.argv[1])
else:
infile = sys.stdin
if len (sys.argv) > 2:
outfile = open (sys.argv[2], "w")
else:
outfile = sys.stdout
path = Path()
path.readpikafile (infile)
path.makesvg()
path.writesvgfile (outfile)

1146
tools/pikatool.c Normal file

File diff suppressed because it is too large Load Diff

141
tools/release-stats.sh Normal file
View File

@ -0,0 +1,141 @@
#!/bin/sh
############################################
# Script gathering statistics on a release #
############################################
#### Usage ####
if [ "$#" -ne 2 -a "$#" -ne 1 ]; then
echo "Usage: $0 <PIKA_TAG_PREV> <PIKA_TAG_CUR>"
echo
echo " PIKA_TAG_PREV: last tag release or commit (non-included in stats)"
echo " ex: PIKA_2_9_6"
echo " PIKA_TAG_CUR: current tag release or commit (included in stats); ex: PIKA_2_9_8"
echo " ex: PIKA_2_9_8."
echo " Optional. If absent, statistics up to HEAD."
exit 1
fi
PREV=$1
git --no-pager show $PREV >/dev/null 2>&1
if [ $? -ne 0 ]; then
echo "First tag is unknown: $PREV"
exit 2
fi
if [ "$#" = 2 ]; then
CUR=$2
git --no-pager show $CUR >/dev/null 2>&1
if [ $? -ne 0 ]; then
echo "Second tag is unknown: $CUR"
exit 2
fi
else
CUR='HEAD'
fi
echo "## PIKA contributions between $PREV and $CUR ##"
echo
# Main stats:
contribs=`git --no-pager shortlog -s -n $PREV..$CUR`
contribs_n=`printf "$contribs" | wc -l`
commits_n=`git log --oneline $PREV..$CUR | wc -l`
prev_date=`git log -1 --format=%ci $PREV`
cur_date=`git log -1 --format=%ci $CUR`
# I think this is not very portable, and may be a bash-specific syntax
# for arithmetic operations. XXX
days_n=$(( (`date -d "$cur_date" +%s` - `date -d "$prev_date" +%s`)/(60*60*24) ))
commits_rate=$(( $commits_n / $days_n ))
echo "Start date: $prev_date - End date: $cur_date"
echo "Between $PREV and $CUR, $contribs_n people contributed $commits_n commits to PIKA."
echo "This is an average of $commits_rate commits a day."
echo
echo "Statistics on all files:" `git diff --shortstat $PREV..$CUR 2>/dev/null`
echo
echo "Total contributor list:"
printf "$contribs"
echo
echo
echo "## DEVELOPERS ##"
echo
echo "Statistics on C files:" `git diff --shortstat $PREV..$CUR -- "*.[ch]" 2>/dev/null`
echo
echo "Core developers:"
git --no-pager shortlog -s -n $PREV..$CUR -- app/ "libpika*" pdb tools/pdbgen/
echo "Plugin and module developers:"
git --no-pager shortlog -s -n $PREV..$CUR -- plug-ins/ modules/
echo
echo "## TRANSLATIONS ##"
echo
#git --no-pager log --stat $PREV..$CUR -- po* | grep "Updated\? .* translation"|sed "s/ *Updated\? \(.*\) translation.*/\\1/" | sort | uniq | paste -s -d, | sed 's/,/, /g'
i18n=`git --no-pager log --stat $PREV..$CUR -- po* | grep "Updated\? .* \(translation\|language\)"`
i18n=`printf "$i18n" | sed "s/ *Updated\? \(.*\) \(translation\|language\).*/\\1/" | sort | uniq`
i18n_n=`printf "$i18n" | wc -l`
# It seems that if the last line has no newline, wc does not count it.
# Add one line manually.
i18n_n=$(( $i18n_n + 1 ))
i18n_comma=`printf "$i18n" | paste -s -d, | sed 's/,/, /g'`
echo "$i18n_n translations were updated: $i18n_comma."
echo
echo "Translators:"
git --no-pager shortlog -sn $PREV..$CUR -- "po*/*.po"
echo
echo "## DESIGN ##"
echo
new_icons=`git log --name-status $PREV..$CUR -- icons/*.svg icons/*.png 2>/dev/null|grep "^A\s"|sed 's%^.*/\([^/]*\)\..*$%\1%' | sort | uniq`
icons_n=$((`printf "$new_icons" | wc -l` + 1))
icons_comma=`printf "$new_icons" | paste -s -d, | sed 's/,/, /g'`
if [ "$icons_n" -lt 20 ]; then
echo "$icons_n icons were added: $icons_comma"
else
echo "$icons_n icons were added (too many to list them all)."
fi
echo
echo "Icon designers:"
git --no-pager shortlog -sn $PREV..$CUR -- "icons/*.svg" "icons/*.png"
echo "Theme designers:"
git --no-pager shortlog -sn $PREV..$CUR -- "themes/*/*.css" "themes/*/*png"
echo "Cursor creators:"
git --no-pager shortlog -sn $PREV..$CUR -- cursors/
echo "Resource creators:"
git --no-pager shortlog -sn $PREV..$CUR -- data/ etc/ desktop/ menus/
echo
echo "## Documenters ##"
echo
git --no-pager shortlog -sn $PREV..$CUR -- docs/ devel-docs/ NEWS INSTALL.in "*README*" HACKING
echo
echo "## Build maintainers ##"
echo
echo "Core autotools build system:"
git --no-pager shortlog -sn $PREV..$CUR -- configure.ac "*/Makefile.am" "tools/"
echo "Meson build system:"
git --no-pager shortlog -sn $PREV..$CUR -- meson_options.txt "*/meson.build"
echo "Gitlab CI:"
git --no-pager shortlog -sn $PREV..$CUR -- .gitlab-ci.yml
echo "Binary builds:"
git --no-pager shortlog -sn $PREV..$CUR -- build/