Further work on animated GIF export: XDG Pictures

Using XDG's user dir settings to determine where pictures are
stored for a user (e.g., "~/Pictures" -- used as a fallback).
May be overridden using "--exportdir".

Also, while I was updating some docs, replace references to
"Mac OS X" with "macOS", the new name of that OS these days.
This commit is contained in:
Bill Kendrick 2020-07-25 16:45:17 -07:00
parent 683bbf5f19
commit f8cce36435
13 changed files with 329 additions and 68 deletions

View file

@ -1,7 +1,7 @@
/*
get_fname.c
Copyright (c) 2009
Copyright (c) 2009 - 2020
http://www.tuxpaint.org/
This program is free software; you can redistribute it and/or modify
@ -30,26 +30,39 @@
#include "debug.h"
#include "compiler.h"
/* DIR_SAVE: Where is the user's saved directory?
This is where their saved files are stored
and where the "current_id.txt" file is saved.
/*
See tuxpaint.c for the OS-specific defaults.
Windows predefines "savedir" as:
"C:\Documents and Settings\%USERNAME%\Application Data\TuxPaint"
though it may get overridden with "--savedir" option
* DIR_SAVE: Where does the user's drawings get saved?
BeOS similarly predefines "savedir" as "./userdata"...
This is where their saved files (PNG) are stored, and where the
"current_id.txt" file is saved (so we can re-load the latest
picture upon a subsequent launch). Generally, end users aren't
expected to access the files in here directly, but they can.
Macintosh: It's under ~/Library/Application Support/TuxPaint
The defaults may be overridden with the "--savedir" option.
Linux & Unix: It's under ~/.tuxpaint
* DIR_DATA: Where is the user's data directory?
DIR_DATA: Where is the user's data directory?
This is where local fonts, brushes and stamps can be found. */
This is where local (user-specific) fonts, brushes, stamps,
starter images, etc., can be found. End users only put things
here if they wish to extend their Tux Paint experience.
The defaults may be overridden with the "--datadir" option.
* DIR_EXPORT: Where does Tux Paint export drawings / animations?
This is where single images, or animated GIF slideshows,
will be exported. It is expected that this is an obvious,
and easily-accessible place for end users to retrieve the exports.
The defaults may be overridden with the "--exportdir" option.
*/
const char *savedir;
const char *datadir;
const char *exportdir;
// FIXME: We shouldn't be allocating memory all the time.
// There should be distinct functions for each directory.
@ -62,16 +75,28 @@ const char *datadir;
* (data file, or saved images?)
*
* @param name Filaneme
* @param kind What kind of file? (DIR_SAVE or DIR_DATA?)
* @param kind What kind of file? (DIR_SAVE, DIR_DATA, or DIR_EXPORT?)
* @return Full fillpath
*/
char *get_fname(const char *const name, int kind)
{
char f[512];
const char *restrict const dir = (kind == DIR_SAVE) ? savedir : datadir;
// const char *restrict const dir;
const char * dir;
// Some mkdir()'s don't like trailing slashes
snprintf(f, sizeof(f), "%s%c%s", dir, (*name) ? '/' : '\0', name);
if (kind == DIR_SAVE) {
dir = savedir;
} else if (kind == DIR_DATA) {
dir = datadir;
} else if (kind == DIR_EXPORT) {
dir = exportdir;
}
snprintf(f, sizeof(f),
"%s%c%s",
dir, (*name) ? '/' : '\0', /* Some mkdir()'s don't like trailing slashes */
name);
return strdup(f);
}

View file

@ -1,7 +1,7 @@
/*
get_fname.h
Copyright (c) 2009
Copyright (c) 2009 - July 25, 2020
http://www.tuxpaint.org/
This program is free software; you can redistribute it and/or modify
@ -27,11 +27,14 @@
extern const char *savedir;
extern const char *datadir;
extern const char *exportdir;
enum
{
/* (See get_fname.c for details) */
DIR_SAVE,
DIR_DATA
DIR_DATA,
DIR_EXPORT
};
char *get_fname(const char *const name, int kind);

View file

@ -112,6 +112,7 @@ datadir, MULTI(datadir)
disablescreensaver, POSBOOL(disable_screensaver)
dontgrab, NEGBOOL(grab_input)
dontmirrorstamps, NEGBOOL(mirrorstamps)
exportdir, MULTI(exportdir)
fancycursors, NEGBOOL(no_fancy_cursors)
fullscreen, MULTI(parsertmp_fullscreen_native)
grab, POSBOOL(grab_input)

View file

@ -22,6 +22,7 @@ struct cfginfo
const char *disable_stamp_controls;
const char *dont_do_xor;
const char *dont_load_stamps;
const char *exportdir;
const char *fullscreen;
const char *grab_input;
const char *hide_cursor;

View file

@ -3,6 +3,8 @@
# Bill Kendrick <bill@newbreedsoftware.com>; http://www.tuxpaint.org/
# Based on inkscape's completion file, by allali@univ-mlv.fr
#
# Last modified 2020-07-25
#
# $Id$
# FIXME: See http://www.debian-administration.org/articles/316 for an intro
@ -57,7 +59,7 @@ _tuxpaint()
--saveoverask --saveover --saveovernew \
--nosave --save \
--autosave --noautosave \
--savedir --datadir \
--savedir --datadir --exportdir \
--printdelay= \
--altprintmod --altprintalways --altprintnever \
--papersize \

View file

@ -22,7 +22,7 @@
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
(See COPYING.txt)
June 14, 2002 - May 23, 2020
June 14, 2002 - July 25, 2020
*/
@ -1317,6 +1317,8 @@ static void handle_motioners(int oldpos_x, int oldpos_y, int motioner, int hatmo
static void handle_joybuttonupdownscl(SDL_Event event, int oldpos_x, int oldpos_y, SDL_Rect real_r_tools);
char * get_xdg_user_dir(const char * dir_type, const char * fallback);
/* Magic tools API and tool handles: */
@ -1977,9 +1979,14 @@ static int do_new_dialog_add_colors(SDL_Surface * *thumbs, int num_files, int *d
char * *d_exts, int *white_in_palette);
static int do_color_picker(void);
static int do_color_sel(void);
static int do_slideshow(void);
static void play_slideshow(int *selected, int num_selected, char *dirname, char **d_names, char **d_exts, int speed);
static void draw_selection_digits(int right, int bottom, int n);
void do_export_gif(void);
char * get_export_filepath(void);
static void wait_for_sfx(void);
static void rgbtohsv(Uint8 r8, Uint8 g8, Uint8 b8, float *h, float *s, float *v);
static void hsvtorgb(float h, float s, float v, Uint8 * r8, Uint8 * g8, Uint8 * b8);
@ -6518,6 +6525,9 @@ void show_usage(int exitcode)
" [--nolockfile]\n"
" [--datadir DIRECTORY]\n"
"\n"
" Exporting:\n"
" [--exportdir DIRECTORY]\n"
"\n"
" Accessibility:\n"
" [--mouse-accessibility]\n"
" [--mouse | --keyboard]\n"
@ -11799,12 +11809,12 @@ static void load_current(void)
* FIXME
*/
/* Make sure we have a 'path' directory */
static int make_directory(const char *path, const char *errmsg)
static int make_directory(int dir_type, const char *path, const char *errmsg)
{
char *fname;
int res;
fname = get_fname(path, DIR_SAVE);
fname = get_fname(path, dir_type);
res = mkdir(fname, 0755);
if (res != 0 && errno != EEXIST)
{
@ -11826,7 +11836,7 @@ static void save_current(void)
char *fname;
FILE *fi;
if (!make_directory("", "Can't create user data directory"))
if (!make_directory(DIR_SAVE, "", "Can't create user data directory"))
{
draw_tux_text(TUX_OOPS, strerror(errno), 0);
return;
@ -13109,7 +13119,7 @@ static int do_save(int tool, int dont_show_success_results)
show_progress_bar(screen);
do_setcursor(cursor_watch);
if (!make_directory("", "Can't create user data directory"))
if (!make_directory(DIR_SAVE, "", "Can't create user data directory"))
{
fprintf(stderr, "Cannot save the any pictures! SORRY!\n\n");
draw_tux_text(TUX_OOPS, strerror(errno), 0);
@ -13121,7 +13131,7 @@ static int do_save(int tool, int dont_show_success_results)
/* Make sure we have a ~/.tuxpaint/saved directory: */
if (!make_directory("saved", "Can't create user data directory"))
if (!make_directory(DIR_SAVE, "saved", "Can't create user data directory"))
{
fprintf(stderr, "Cannot save any pictures! SORRY!\n\n");
draw_tux_text(TUX_OOPS, strerror(errno), 0);
@ -13133,7 +13143,7 @@ static int do_save(int tool, int dont_show_success_results)
/* Make sure we have a ~/.tuxpaint/saved/.thumbs/ directory: */
if (!make_directory("saved/.thumbs", "Can't create user data thumbnail directory"))
if (!make_directory(DIR_SAVE, "saved/.thumbs", "Can't create user data thumbnail directory"))
{
fprintf(stderr, "Cannot save any pictures! SORRY!\n\n");
draw_tux_text(TUX_OOPS, strerror(errno), 0);
@ -13144,7 +13154,7 @@ static int do_save(int tool, int dont_show_success_results)
/* Make sure we have a ~/.tuxpaint/saved/.label/ directory: */
if (!make_directory("saved/.label", "Can't create label information directory"))
if (!make_directory(DIR_SAVE, "saved/.label", "Can't create label information directory"))
{
fprintf(stderr, "Cannot save label information! SORRY!\n\n");
draw_tux_text(TUX_OOPS, strerror(errno), 0);
@ -14181,10 +14191,10 @@ static int do_open(void)
/* No thumbnail - load original: */
/* Make sure we have a ~/.tuxpaint/saved directory: */
if (make_directory("saved", "Can't create user data directory"))
if (make_directory(DIR_SAVE, "saved", "Can't create user data directory"))
{
/* (Make sure we have a .../saved/.thumbs/ directory:) */
make_directory("saved/.thumbs", "Can't create user data thumbnail directory");
make_directory(DIR_SAVE, "saved/.thumbs", "Can't create user data thumbnail directory");
}
@ -15202,10 +15212,10 @@ static int do_slideshow(void)
/* No thumbnail - load original: */
/* Make sure we have a ~/.tuxpaint/saved directory: */
if (make_directory("saved", "Can't create user data directory"))
if (make_directory(DIR_SAVE, "saved", "Can't create user data directory"))
{
/* (Make sure we have a .../saved/.thumbs/ directory:) */
make_directory("saved/.thumbs", "Can't create user data thumbnail directory");
make_directory(DIR_SAVE, "saved/.thumbs", "Can't create user data thumbnail directory");
}
snprintf(fname, sizeof(fname), "%s/%s", dirname, f->d_name);
@ -15676,7 +15686,7 @@ static int do_slideshow(void)
}
else
{
/* FIXME: Do it */
do_export_gif();
}
}
else if (event.button.x >= (WINDOW_WIDTH - 96 - 48) &&
@ -18998,11 +19008,11 @@ static int do_new_dialog(void)
/* No thumbnail - load original: */
/* Make sure we have a ~/.tuxpaint/[starters|templates] directory: */
if (make_directory(dirname[d_places[num_files]], "Can't create user data directory"))
if (make_directory(DIR_SAVE, dirname[d_places[num_files]], "Can't create user data directory"))
{
/* (Make sure we have a .../[starters|templates]/.thumbs/ directory:) */
snprintf(fname, sizeof(fname), "%s/.thumbs", dirname[d_places[num_files]]);
make_directory(fname, "Can't create user data thumbnail directory");
make_directory(DIR_SAVE, fname, "Can't create user data thumbnail directory");
}
img = NULL;
@ -19103,10 +19113,10 @@ static int do_new_dialog(void)
snprintf(fname, sizeof(fname), "%s/.thumbs/%s-t.png",
dirname[d_places[num_files]], d_names[num_files]);
if (!make_directory("starters", "Can't create user data directory") ||
!make_directory("templates", "Can't create user data directory") ||
!make_directory("starters/.thumbs", "Can't create user data directory") ||
!make_directory("templates/.thumbs", "Can't create user data directory"))
if (!make_directory(DIR_SAVE, "starters", "Can't create user data directory") ||
!make_directory(DIR_SAVE, "templates", "Can't create user data directory") ||
!make_directory(DIR_SAVE, "starters/.thumbs", "Can't create user data directory") ||
!make_directory(DIR_SAVE, "templates/.thumbs", "Can't create user data directory"))
fprintf(stderr, "Cannot save any pictures! SORRY!\n\n");
else
{
@ -22565,6 +22575,7 @@ static void setup_config(char *argv[])
}
#endif
/* == SAVEDIR == */
if (tmpcfg_cmd.savedir)
savedir = strdup(tmpcfg_cmd.savedir);
else
@ -22595,6 +22606,23 @@ static void setup_config(char *argv[])
#endif
}
/* == EXPORTDIR == */
if (tmpcfg_cmd.exportdir)
exportdir = strdup(tmpcfg_cmd.exportdir);
else
{
/* FIXME: Need assist for:
* _WIN32
* __BEOS__
* __HAIKU__
* __APPLE__
*/
exportdir = get_xdg_user_dir("PICTURES", "Pictures");
}
printf("Export Dir = %s\n", exportdir);
exit(0);
/* Load options from user's own configuration (".rc" / ".cfg") file: */
#if defined(_WIN32)
@ -24635,6 +24663,7 @@ static int trash(char *path)
/* Move file into Trash folder */
/* FIXME: Use xdg function */
if (getenv("XDG_DATA_HOME") != NULL)
{
sprintf(trashpath, "%s/Trash", getenv("XDG_DATA_HOME"));
@ -25114,3 +25143,129 @@ static void handle_joybuttonupdownscl(SDL_Event event, int oldpos_x, int oldpos_
if (!ignore)
SDL_PushEvent(&ev);
}
/**
* Grab the user's XDG user dir for something (e.g., ~/Pictures)
*
* @param char * dir_type -- the thing to query, e.g. "PICTURES" or "VIDEOS"
* (note: currently, Tux Paint only puts things in the PICTURES one)
* @param char * fallback -- path, under $HOME, to use instead (e.g., "Pictures")
* @return char * path (caller is expected to free() it)
*/
char * get_xdg_user_dir(const char * dir_type, const char * fallback) {
FILE * fi;
char * config_home, * found;
char tmp_path[MAX_PATH], config_path[MAX_PATH], line[MAX_PATH], search[MAX_PATH], return_path[MAX_PATH];
int found_it;
found_it = FALSE;
/* Figure out where XDG user-dirs config exists, and use it if possible */
if (getenv("XDG_CONFIG_HOME") != NULL) {
config_home = strdup(getenv("XDG_CONFIG_HOME"));
} else {
#ifdef DEBUG
fprintf(stderr, "XDG_CONFIG_HOME not set, checking $HOME/.config/\n");
#endif
if (getenv("HOME") != NULL) {
snprintf(tmp_path, MAX_PATH, "%s/.config", getenv("HOME"));
config_home = strdup(tmp_path);
} else {
#ifdef DEBUG
fprintf(stderr, "No HOME, either?! Returing fallback in current directory\n");
#endif
return strdup(fallback);
}
}
if (config_home[strlen(config_home) - 1] == '/') {
config_home[strlen(config_home) - 1] = '\0';
}
snprintf(config_path, MAX_PATH, "%s/user-dirs.dirs", config_home);
free(config_home);
#ifdef DEBUG
fprintf(stderr, "User dirs config = %s\n", config_path);
#endif
snprintf(search, MAX_PATH, "XDG_%s_DIR=\"", dir_type);
/* Read the config to find the path we want */
fi = fopen(config_path, "r");
if (fi != NULL) {
/* Search for a line in the form of either
either XDG_PICTURES_DIR="$HOME/Pictures"
or XDG_PICTURES_DIR="/Path/To/Pictures"
*/
#ifdef DEBUG
fprintf(stderr, "Searching it for: %s\n", search);
#endif
while (fgets(line, MAX_PATH, fi) && !found_it) {
/* Trim trailing CR/LF */
if (line[strlen(line) - 1] == '\n' ||
line[strlen(line) - 1] == '\r') {
line[strlen(line) - 1] = '\0';
}
if (strstr(line, search) == line) {
found = line + strlen(search);
#ifdef DEBUG
fprintf(stderr, "Found it: %s\n", found);
#endif
if (strstr(found, "$HOME/") == found) {
snprintf(return_path, MAX_PATH, "%s/%s", getenv("HOME"), found + 6 /* skip '$HOME/' */);
} else {
strcpy(return_path, found);
}
/* Trim trailing " */
if (return_path[strlen(return_path) - 1] == '\"') {
return_path[strlen(return_path) - 1] = '\0';
}
found_it = TRUE;
}
}
fclose(fi);
#ifdef DEBUG
} else {
fprintf(stderr, "%s doesn't exist\n", config_path);
#endif
}
if (!found_it) {
#ifdef DEBUG
fprintf(stderr, "Using fallback of $HOME/%s\n", fallback);
#endif
snprintf(return_path, MAX_PATH, "%s/%s", getenv("HOME"), fallback);
}
#ifdef DEBUG
fprintf(stderr, "Location for %s => %s\n", dir_type, return_path);
#endif
return strdup(return_path);
}
/**
* After 2+ images have been selected in the Open->Slideshow
* dialog, they can be exported as an animated GIF.
*/
void do_export_gif(void) {
}
/**
* Returns the user's chosen export directory
* for animated GIFs, via Open->Slideshow dialog,
* and static PNGs, via Open dialog */
char * get_export_filepath(void) {
char *rname;
char fname[FILENAME_MAX];
rname = NULL;
/*
snprintf(fname, sizeof(fname), "saved/%s.dat", saved_id);
rname = get_fname(fname, DIR_SAVE);
*/
}

View file

@ -7,7 +7,7 @@
# http://www.tuxpaint.org/
#
# Default distribution version last modified:
# September 21, 2019
# July 25, 2020
#
# $Id$
@ -251,6 +251,13 @@
# savedir=~/.tuxpaint
### Export images and animated GIF slideshows somewhere different?
### (Uses xdg-user-dirs config setting for PICTURES, if available!)
### --------------------------------------------------------------
#
# exportdir=~/Pictures
### Use a different language?
### -------------------------
### Note: Where the language is a known language name (e.g., "spanish")