Compare commits

..

No commits in common. "master" and "0.1" have entirely different histories.
master ... 0.1

14 changed files with 599 additions and 715 deletions

44
LICENSE
View File

@ -1,29 +1,23 @@
ISC-License The MIT License (MIT)
(c) 2014-2017 Markus Teich <markus.teich@stusta.mhn.de> Copyright (c) 2014-2015 Markus Teich
Permission to use, copy, modify, and/or distribute this software for any png handling stuff adapted from meh by John Hawthorn
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES Permission is hereby granted, free of charge, to any person obtaining a copy
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF of this software and associated documentation files (the "Software"), to deal
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR in the Software without restriction, including without limitation the rights
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN copies of the Software, and to permit persons to whom the Software is
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF furnished to do so, subject to the following conditions:
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
(c) 2016,2017 Laslo Hunhold <dev@frign.de> The above copyright notice and this permission notice shall be included in all
(c) 2016 ssd <ssd@mailless.org> copies or substantial portions of the Software.
(c) 2016 Hiltjo Posthuma <hiltjo@codemadness.org>
(c) 2015 David Phillips <dbphillipsnz@gmail.com> THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
(c) 2015 Grant Mathews <grant.m.mathews@gmail.com> IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
(c) 2015 Dimitris Papastamos <sin@2f30.org> FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
(c) 2015 Alexis <surryhill@gmail.com> AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
(c) 2015 Quentin Rameau <quinq@fifth.space> LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
(c) 2015 Ivan Tham <pickfire@riseup.net> OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
(c) 2015 Jan Christoph Ebersbach <jceb@e-jc.de> SOFTWARE.
(c) 2015 Tony Lainson <t.lainson@gmail.com>
(c) 2015 Szabolcs Nagy <nsz@port70.net>
(c) 2015 Jonas Jelten <jj@sft.mx>

View File

@ -18,35 +18,39 @@ config.h:
cp config.def.h config.h cp config.def.h config.h
.c.o: .c.o:
${CC} -c ${CFLAGS} $< @echo CC $<
@${CC} -c ${CFLAGS} $<
${OBJ}: config.h config.mk ${OBJ}: config.h config.mk
sent: ${OBJ} sent: ${OBJ}
${CC} -o $@ ${OBJ} ${LDFLAGS} @echo CC -o $@
@${CC} -o $@ ${OBJ} ${LDFLAGS}
cscope: ${SRC} config.h cscope: ${SRC} config.h
cscope -R -b || echo cScope not installed @echo cScope
@cscope -R -b || echo cScope not installed
clean: clean:
rm -f sent ${OBJ} sent-${VERSION}.tar.gz @echo cleaning
@rm -f sent ${OBJ} sent-${VERSION}.tar.gz
dist: clean dist: clean
mkdir -p sent-${VERSION} @echo creating dist tarball
cp -R LICENSE Makefile config.mk config.def.h ${SRC} sent-${VERSION} @mkdir -p sent-${VERSION}
tar -cf sent-${VERSION}.tar sent-${VERSION} @cp -R LICENSE Makefile config.mk config.def.h ${SRC} sent-${VERSION}
gzip sent-${VERSION}.tar @tar -cf sent-${VERSION}.tar sent-${VERSION}
rm -rf sent-${VERSION} @gzip sent-${VERSION}.tar
@rm -rf sent-${VERSION}
install: all install: all
mkdir -p ${DESTDIR}${PREFIX}/bin @echo installing executable file to ${DESTDIR}${PREFIX}/bin
cp -f sent ${DESTDIR}${PREFIX}/bin @mkdir -p ${DESTDIR}${PREFIX}/bin
chmod 755 ${DESTDIR}${PREFIX}/bin/sent @cp -f sent ${DESTDIR}${PREFIX}/bin
mkdir -p ${DESTDIR}${MANPREFIX}/man1 @chmod 755 ${DESTDIR}${PREFIX}/bin/sent
cp sent.1 ${DESTDIR}${MANPREFIX}/man1/sent.1
chmod 644 ${DESTDIR}${MANPREFIX}/man1/sent.1
uninstall: uninstall:
rm -f ${DESTDIR}${PREFIX}/bin/sent @echo removing executable file from ${DESTDIR}${PREFIX}/bin
@rm -f ${DESTDIR}${PREFIX}/bin/sent
.PHONY: all options clean dist install uninstall cscope .PHONY: all options clean dist install uninstall cscope

View File

@ -1,59 +1,70 @@
sent is a simple plaintext presentation tool. sent
====
A simple plaintext presentation tool.
sent does not need latex, libreoffice or any other fancy file format, it uses sent does not need latex, libreoffice or any other fancy file format, it uses
plaintext files to describe the slides and can include images via farbfeld. plaintext files and png images. Every line represents a slide in the
Every paragraph represents a slide in the presentation. presentation. This may limit the use, but for presentations using the [Takahashi
method](https://en.wikipedia.org/wiki/Takahashi_method) this is very nice and
allows you to write down the presentation for a quick lightning talk within a
few minutes.
The presentation is displayed in a simple X11 window. The content of each slide The presentation is displayed in a simple X11 window colored black on white for
is automatically scaled to fit the window and centered so you also don't have to maximum contrast even if the sun shines directly onto the projected image. The
worry about alignment. Instead you can really concentrate on the content. content of each slide is automatically scaled to fit the window so you don't
have to worry about alignment. Instead you can really concentrate on the
content.
Dependencies
You need Xlib and Xft to build sent and the farbfeld[0] tools installed to use
images in your presentations.
Demo Demo
----
To get a little demo, just type To get a little demo, just type
make && ./sent example make && ./sent example
You can navigate with the arrow keys and quit with `q`. You can navigate with the arrow keys and quit with `q`. If you get
sent: could not find a scalable font matching -*-dejavu sans condensed-bold-r-*-*-0-0-*-*-*-0-*-*
you should add the dejavu fonts dir (customize path to fit your distribution)
with:
xset fp+ /usr/share/fonts/dejavu
Configuration
-------------
Edit config.h to fit your needs. The font has to be in the X servers font path,
see `man xset` for how to add it.
Usage Usage
-----
sent [FILE] sent [-f FONTSTRING] FILE1 [FILE2 ...]
If FILE is omitted or equals `-`, stdin will be read. Produce image slides by If one FILE equals `-`, stdin will be read. Use png images by prepending a `@`
prepending a `@` in front of the filename as a single paragraph. Lines starting before the filename. Lines starting with `#` will be ignored. A presentation
with `#` will be ignored. A `\` at the beginning of the line escapes `@` and file could look like this:
`#`. A presentation file could look like this:
sent sent
why?
@nyan.png @nyan.png
easy to use
depends on depends on Xlib, libpng
- Xlib no bloat
- Xft how?
- farbfeld
sent FILENAME sent FILENAME
one slide per paragraph one slide per line
# This is a comment and will not be part of the presentation # This is a comment and will not be part of the presentation
\# This and the next line start with backslashes # The next line starts with a whitespace, it will not produce an image slide
@FILE.png
\@FILE.png
thanks / questions? thanks / questions?
future features
---------------
Development * multiple lines per slide?
* light colored background and table of contents
sent is developed at http://tools.suckless.org/sent * second window for speakers laptop (progress, time, notes?)
* markdown?
0: http://tools.suckless.org/farbfeld/

98
arg.h
View File

@ -1,49 +1,63 @@
/* /*
* ISC-License * Copy me if you can.
* * by 20h
* Copyright 2017 Laslo Hunhold <dev@frign.de>
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/ */
#ifndef ARG_H
#define ARG_H #ifndef ARG_H__
#define ARG_H__
extern char *argv0; extern char *argv0;
/* int main(int argc, char *argv[]) */ /* use main(int argc, char *argv[]) */
#define ARGBEGIN for (argv0 = *argv, *argv ? (argc--, argv++) : ((void *)0); \ #define ARGBEGIN for (argv0 = *argv, argv++, argc--;\
*argv && (*argv)[0] == '-' && (*argv)[1]; argc--, argv++) { \ argv[0] && argv[0][1]\
int i_, argused_; \ && argv[0][0] == '-';\
if ((*argv)[1] == '-' && !(*argv)[2]) { \ argc--, argv++) {\
argc--, argv++; \ char argc_;\
break; \ char **argv_;\
} \ int brk_;\
for (i_ = 1, argused_ = 0; (*argv)[i_]; i_++) { \ if (argv[0][1] == '-' && argv[0][2] == '\0') {\
switch((*argv)[i_]) argv++;\
#define ARGEND if (argused_) { \ argc--;\
if ((*argv)[i_ + 1]) { \ break;\
break; \ }\
} else { \ for (brk_ = 0, argv[0]++, argv_ = argv;\
argc--, argv++; \ argv[0][0] && !brk_;\
break; \ argv[0]++) {\
} \ if (argv_ != argv)\
} \ break;\
} \ argc_ = argv[0][0];\
} switch (argc_)
#define ARGC() ((*argv)[i_])
#define ARGF_(x) (((*argv)[i_ + 1]) ? (argused_ = 1, &((*argv)[i_ + 1])) : \ /* Handles obsolete -NUM syntax */
(*(argv + 1)) ? (argused_ = 1, *(argv + 1)) : (x)) #define ARGNUM case '0':\
#define EARGF(x) ARGF_(((x), exit(1), (char *)0)) case '1':\
#define ARGF() ARGF_((char *)0) case '2':\
case '3':\
case '4':\
case '5':\
case '6':\
case '7':\
case '8':\
case '9'
#define ARGEND }\
}
#define ARGC() argc_
#define ARGNUMF(base) (brk_ = 1, estrtol(argv[0], (base)))
#define EARGF(x) ((argv[0][1] == '\0' && argv[1] == NULL)?\
((x), abort(), (char *)0) :\
(brk_ = 1, (argv[0][1] != '\0')?\
(&argv[0][1]) :\
(argc--, argv++, argv[0])))
#define ARGF() ((argv[0][1] == '\0' && argv[1] == NULL)?\
(char *)0 :\
(brk_ = 1, (argv[0][1] != '\0')?\
(&argv[0][1]) :\
(argc--, argv++, argv[0])))
#endif #endif

View File

@ -1,17 +1,15 @@
/* See LICENSE file for copyright and license details. */ /* See LICENSE file for copyright and license details. */
static char *fontfallbacks[] = { static char *fontfallbacks[] = {
"dejavu sans", "dejavu",
"roboto", "roboto",
"ubuntu", "ubuntu",
}; };
#define NUMFONTSCALES 42 #define NUMFONTSCALES 30
#define FONTSZ(x) ((int)(10.0 * powf(1.1288, (x)))) /* x in [0, NUMFONTSCALES-1] */ #define FONTSZ(x) ((int)(10.0 * powf(1.1288, (x)))) /* x in [0, NUMFONTSCALES-1] */
static const char *colors[] = { static const char *fgcol = "#000000";
"#000000", /* foreground color */ static const char *bgcol = "#FFFFFF";
"#FFFFFF", /* background color */
};
static const float linespacing = 1.4; static const float linespacing = 1.4;
@ -22,19 +20,15 @@ static const float usableheight = 0.75;
static Mousekey mshortcuts[] = { static Mousekey mshortcuts[] = {
/* button function argument */ /* button function argument */
{ Button1, advance, {.i = +1} }, { Button1, advance, {.i = +1} },
{ Button3, advance, {.i = -1} }, { Button2, advance, {.i = -1} },
{ Button4, advance, {.i = -1} },
{ Button5, advance, {.i = +1} },
}; };
static Shortcut shortcuts[] = { static Shortcut shortcuts[] = {
/* keysym function argument */ /* keysym function argument */
{ XK_Escape, quit, {0} },
{ XK_q, quit, {0} }, { XK_q, quit, {0} },
{ XK_Right, advance, {.i = +1} }, { XK_Right, advance, {.i = +1} },
{ XK_Left, advance, {.i = -1} }, { XK_Left, advance, {.i = -1} },
{ XK_Return, advance, {.i = +1} }, { XK_Return, advance, {.i = +1} },
{ XK_space, advance, {.i = +1} },
{ XK_BackSpace, advance, {.i = -1} }, { XK_BackSpace, advance, {.i = -1} },
{ XK_l, advance, {.i = +1} }, { XK_l, advance, {.i = +1} },
{ XK_h, advance, {.i = -1} }, { XK_h, advance, {.i = -1} },
@ -44,13 +38,4 @@ static Shortcut shortcuts[] = {
{ XK_Up, advance, {.i = -1} }, { XK_Up, advance, {.i = -1} },
{ XK_Next, advance, {.i = +1} }, { XK_Next, advance, {.i = +1} },
{ XK_Prior, advance, {.i = -1} }, { XK_Prior, advance, {.i = -1} },
{ XK_n, advance, {.i = +1} },
{ XK_p, advance, {.i = -1} },
{ XK_r, reload, {0} },
};
static Filter filters[] = {
{ "\\.ff$", "cat" },
{ "\\.ff.bz2$", "bunzip2" },
{ "\\.[a-z0-9]+$", "2ff" },
}; };

View File

@ -1,5 +1,5 @@
# sent version # sent version
VERSION = 1 VERSION = 0.1
# Customize below to fit your system # Customize below to fit your system
@ -12,12 +12,7 @@ X11LIB = /usr/X11R6/lib
# includes and libs # includes and libs
INCS = -I. -I/usr/include -I/usr/include/freetype2 -I${X11INC} INCS = -I. -I/usr/include -I/usr/include/freetype2 -I${X11INC}
LIBS = -L/usr/lib -lc -lm -L${X11LIB} -lXft -lfontconfig -lX11 LIBS = -L/usr/lib -lc -lm -L${X11LIB} -lXft -lfontconfig -lX11 -lpng
# OpenBSD (uncomment)
#INCS = -I. -I${X11INC} -I${X11INC}/freetype2
# FreeBSD (uncomment)
#INCS = -I. -I/usr/local/include -I/usr/local/include/freetype2 -I${X11INC}
#LIBS = -L/usr/local/lib -lc -lm -L${X11LIB} -lXft -lfontconfig -lX11
# flags # flags
CPPFLAGS = -DVERSION=\"${VERSION}\" -D_XOPEN_SOURCE=600 CPPFLAGS = -DVERSION=\"${VERSION}\" -D_XOPEN_SOURCE=600

328
drw.c
View File

@ -9,7 +9,9 @@
#include "util.h" #include "util.h"
#define UTF_INVALID 0xFFFD #define UTF_INVALID 0xFFFD
#define UTF_SIZ 4 #define UTF_SIZ 4
static void drw_xfont_free(Fnt *font);
static const unsigned char utfbyte[UTF_SIZ + 1] = {0x80, 0, 0xC0, 0xE0, 0xF0}; static const unsigned char utfbyte[UTF_SIZ + 1] = {0x80, 0, 0xC0, 0xE0, 0xF0};
static const unsigned char utfmask[UTF_SIZ + 1] = {0xC0, 0x80, 0xE0, 0xF0, 0xF8}; static const unsigned char utfmask[UTF_SIZ + 1] = {0xC0, 0x80, 0xE0, 0xF0, 0xF8};
@ -17,54 +19,50 @@ static const long utfmin[UTF_SIZ + 1] = { 0, 0, 0x80, 0x800, 0x10000
static const long utfmax[UTF_SIZ + 1] = {0x10FFFF, 0x7F, 0x7FF, 0xFFFF, 0x10FFFF}; static const long utfmax[UTF_SIZ + 1] = {0x10FFFF, 0x7F, 0x7FF, 0xFFFF, 0x10FFFF};
static long static long
utf8decodebyte(const char c, size_t *i) utf8decodebyte(const char c, size_t *i) {
{ for(*i = 0; *i < (UTF_SIZ + 1); ++(*i))
for (*i = 0; *i < (UTF_SIZ + 1); ++(*i)) if(((unsigned char)c & utfmask[*i]) == utfbyte[*i])
if (((unsigned char)c & utfmask[*i]) == utfbyte[*i])
return (unsigned char)c & ~utfmask[*i]; return (unsigned char)c & ~utfmask[*i];
return 0; return 0;
} }
static size_t static size_t
utf8validate(long *u, size_t i) utf8validate(long *u, size_t i) {
{ if(!BETWEEN(*u, utfmin[i], utfmax[i]) || BETWEEN(*u, 0xD800, 0xDFFF))
if (!BETWEEN(*u, utfmin[i], utfmax[i]) || BETWEEN(*u, 0xD800, 0xDFFF))
*u = UTF_INVALID; *u = UTF_INVALID;
for (i = 1; *u > utfmax[i]; ++i) for(i = 1; *u > utfmax[i]; ++i)
; ;
return i; return i;
} }
static size_t static size_t
utf8decode(const char *c, long *u, size_t clen) utf8decode(const char *c, long *u, size_t clen) {
{
size_t i, j, len, type; size_t i, j, len, type;
long udecoded; long udecoded;
*u = UTF_INVALID; *u = UTF_INVALID;
if (!clen) if(!clen)
return 0; return 0;
udecoded = utf8decodebyte(c[0], &len); udecoded = utf8decodebyte(c[0], &len);
if (!BETWEEN(len, 1, UTF_SIZ)) if(!BETWEEN(len, 1, UTF_SIZ))
return 1; return 1;
for (i = 1, j = 1; i < clen && j < len; ++i, ++j) { for(i = 1, j = 1; i < clen && j < len; ++i, ++j) {
udecoded = (udecoded << 6) | utf8decodebyte(c[i], &type); udecoded = (udecoded << 6) | utf8decodebyte(c[i], &type);
if (type) if(type != 0)
return j; return j;
} }
if (j < len) if(j < len)
return 0; return 0;
*u = udecoded; *u = udecoded;
utf8validate(u, len); utf8validate(u, len);
return len; return len;
} }
Drw * Drw *
drw_create(Display *dpy, int screen, Window root, unsigned int w, unsigned int h) drw_create(Display *dpy, int screen, Window root, unsigned int w, unsigned int h) {
{ Drw *drw = (Drw *)calloc(1, sizeof(Drw));
Drw *drw = ecalloc(1, sizeof(Drw)); if(!drw)
return NULL;
drw->dpy = dpy; drw->dpy = dpy;
drw->screen = screen; drw->screen = screen;
drw->root = root; drw->root = root;
@ -73,26 +71,22 @@ drw_create(Display *dpy, int screen, Window root, unsigned int w, unsigned int h
drw->drawable = XCreatePixmap(dpy, root, w, h, DefaultDepth(dpy, screen)); drw->drawable = XCreatePixmap(dpy, root, w, h, DefaultDepth(dpy, screen));
drw->gc = XCreateGC(dpy, root, 0, NULL); drw->gc = XCreateGC(dpy, root, 0, NULL);
XSetLineAttributes(dpy, drw->gc, 1, LineSolid, CapButt, JoinMiter); XSetLineAttributes(dpy, drw->gc, 1, LineSolid, CapButt, JoinMiter);
return drw; return drw;
} }
void void
drw_resize(Drw *drw, unsigned int w, unsigned int h) drw_resize(Drw *drw, unsigned int w, unsigned int h) {
{ if(!drw)
if (!drw)
return; return;
drw->w = w; drw->w = w;
drw->h = h; drw->h = h;
if (drw->drawable) if(drw->drawable != 0)
XFreePixmap(drw->dpy, drw->drawable); XFreePixmap(drw->dpy, drw->drawable);
drw->drawable = XCreatePixmap(drw->dpy, drw->root, w, h, DefaultDepth(drw->dpy, drw->screen)); drw->drawable = XCreatePixmap(drw->dpy, drw->root, w, h, DefaultDepth(drw->dpy, drw->screen));
} }
void void
drw_free(Drw *drw) drw_free(Drw *drw) {
{
XFreePixmap(drw->dpy, drw->drawable); XFreePixmap(drw->dpy, drw->drawable);
XFreeGC(drw->dpy, drw->gc); XFreeGC(drw->dpy, drw->gc);
free(drw); free(drw);
@ -102,123 +96,119 @@ drw_free(Drw *drw)
* drw_fontset_create instead. * drw_fontset_create instead.
*/ */
static Fnt * static Fnt *
xfont_create(Drw *drw, const char *fontname, FcPattern *fontpattern) drw_font_xcreate(Drw *drw, const char *fontname, FcPattern *fontpattern) {
{
Fnt *font; Fnt *font;
XftFont *xfont = NULL;
FcPattern *pattern = NULL; if (!(fontname || fontpattern))
die("No font specified.\n");
if (!(font = (Fnt *)calloc(1, sizeof(Fnt))))
return NULL;
if (fontname) { if (fontname) {
/* Using the pattern found at font->xfont->pattern does not yield the /* Using the pattern found at font->xfont->pattern does not yield the
* same substitution results as using the pattern returned by * same substitution results as using the pattern returned by
* FcNameParse; using the latter results in the desired fallback * FcNameParse; using the latter results in the desired fallback
* behaviour whereas the former just results in missing-character * behaviour whereas the former just results in
* rectangles being drawn, at least with some fonts. */ * missing-character-rectangles being drawn, at least with some fonts.
if (!(xfont = XftFontOpenName(drw->dpy, drw->screen, fontname))) { */
fprintf(stderr, "error, cannot load font from name: '%s'\n", fontname); if (!(font->xfont = XftFontOpenName(drw->dpy, drw->screen, fontname)) ||
return NULL; !(font->pattern = FcNameParse((const FcChar8 *) fontname))) {
} if (font->xfont) {
if (!(pattern = FcNameParse((FcChar8 *) fontname))) { XftFontClose(drw->dpy, font->xfont);
fprintf(stderr, "error, cannot parse font name to pattern: '%s'\n", fontname); font->xfont = NULL;
XftFontClose(drw->dpy, xfont); }
return NULL; fprintf(stderr, "error, cannot load font: '%s'\n", fontname);
} }
} else if (fontpattern) { } else if (fontpattern) {
if (!(xfont = XftFontOpenPattern(drw->dpy, fontpattern))) { if (!(font->xfont = XftFontOpenPattern(drw->dpy, fontpattern))) {
fprintf(stderr, "error, cannot load font from pattern.\n"); fprintf(stderr, "error, cannot load font pattern.\n");
return NULL; } else {
font->pattern = NULL;
} }
} else {
die("no font specified.");
} }
font = ecalloc(1, sizeof(Fnt)); if (!font->xfont) {
font->xfont = xfont; free(font);
font->pattern = pattern; return NULL;
font->h = xfont->ascent + xfont->descent; }
font->dpy = drw->dpy;
font->ascent = font->xfont->ascent;
font->descent = font->xfont->descent;
font->h = font->ascent + font->descent;
font->dpy = drw->dpy;
return font; return font;
} }
static void void
xfont_free(Fnt *font) drw_xfont_free(Fnt *font) {
{ if(!font)
if (!font)
return; return;
if (font->pattern) if(font->pattern)
FcPatternDestroy(font->pattern); FcPatternDestroy(font->pattern);
XftFontClose(font->dpy, font->xfont); XftFontClose(font->dpy, font->xfont);
free(font); free(font);
} }
Fnt* Fnt*
drw_fontset_create(Drw* drw, const char *fonts[], size_t fontcount) drw_fontset_create(Drw* drw, const char *fonts[], size_t fontcount) {
{ Fnt *ret = NULL;
Fnt *cur, *ret = NULL; Fnt *cur = NULL;
size_t i; ssize_t i;
for (i = fontcount - 1; i >= 0; i--) {
if (!drw || !fonts) if ((cur = drw_font_xcreate(drw, fonts[i], NULL))) {
return NULL;
for (i = 1; i <= fontcount; i++) {
if ((cur = xfont_create(drw, fonts[fontcount - i], NULL))) {
cur->next = ret; cur->next = ret;
ret = cur; ret = cur;
} }
} }
return (drw->fonts = ret);
}
void
drw_fontset_free(Fnt *font)
{
if (font) {
drw_fontset_free(font->next);
xfont_free(font);
}
}
void
drw_clr_create(Drw *drw, Clr *dest, const char *clrname)
{
if (!drw || !dest || !clrname)
return;
if (!XftColorAllocName(drw->dpy, DefaultVisual(drw->dpy, drw->screen),
DefaultColormap(drw->dpy, drw->screen),
clrname, dest))
die("error, cannot allocate color '%s'", clrname);
}
/* Wrapper to create color schemes. The caller has to call free(3) on the
* returned color scheme when done using it. */
Clr *
drw_scm_create(Drw *drw, const char *clrnames[], size_t clrcount)
{
size_t i;
Clr *ret;
/* need at least two colors for a scheme */
if (!drw || !clrnames || clrcount < 2 || !(ret = ecalloc(clrcount, sizeof(XftColor))))
return NULL;
for (i = 0; i < clrcount; i++)
drw_clr_create(drw, &ret[i], clrnames[i]);
return ret; return ret;
} }
void void
drw_setfontset(Drw *drw, Fnt *set) drw_fontset_free(Fnt *font) {
{ Fnt *nf = font;
while ((font = nf)) {
nf = font->next;
drw_xfont_free(font);
}
}
Scm *
drw_scm_create(Drw *drw, const char *fgname, const char *bgname) {
Scm *scm;
Colormap cmap;
Visual *vis;
if (!drw || !(scm = (Scm *)calloc(1, sizeof(Scm))))
return NULL;
cmap = DefaultColormap(drw->dpy, drw->screen);
vis = DefaultVisual(drw->dpy, drw->screen);
if (!XftColorAllocName(drw->dpy, vis, cmap, fgname, &scm->fg.rgb))
die("error, cannot allocate color '%s'\n", fgname);
if (!XftColorAllocName(drw->dpy, vis, cmap, bgname, &scm->bg.rgb))
die("error, cannot allocate color '%s'\n", bgname);
scm->fg.pix = scm->fg.rgb.pixel;
scm->bg.pix = scm->bg.rgb.pixel;
return scm;
}
void
drw_scm_free(Scm *scm) {
if (scm)
free(scm);
}
void
drw_setfontset(Drw *drw, Fnt *set) {
if (drw) if (drw)
drw->fonts = set; drw->fonts = set;
} }
void void
drw_setscheme(Drw *drw, Clr *scm) drw_setscheme(Drw *drw, Scm *scm) {
{ if (drw && scm)
if (drw)
drw->scheme = scm; drw->scheme = scm;
} }
@ -227,23 +217,24 @@ drw_rect(Drw *drw, int x, int y, unsigned int w, unsigned int h, int filled, int
{ {
if (!drw || !drw->scheme) if (!drw || !drw->scheme)
return; return;
XSetForeground(drw->dpy, drw->gc, invert ? drw->scheme[ColBg].pixel : drw->scheme[ColFg].pixel); XSetForeground(drw->dpy, drw->gc, invert ? drw->scheme->bg.pix : drw->scheme->fg.pix);
if (filled) if (filled)
XFillRectangle(drw->dpy, drw->drawable, drw->gc, x, y, w, h); XFillRectangle(drw->dpy, drw->drawable, drw->gc, x, y, w, h);
else else
XDrawRectangle(drw->dpy, drw->drawable, drw->gc, x, y, w - 1, h - 1); XDrawRectangle(drw->dpy, drw->drawable, drw->gc, x, y, w, h);
} }
int int
drw_text(Drw *drw, int x, int y, unsigned int w, unsigned int h, unsigned int lpad, const char *text, int invert) drw_text(Drw *drw, int x, int y, unsigned int w, unsigned int h, const char *text, int invert) {
{
char buf[1024]; char buf[1024];
int ty; int tx, ty, th;
unsigned int ew; unsigned int ew;
XftDraw *d = NULL; Colormap cmap;
Visual *vis;
XftDraw *d;
Fnt *usedfont, *curfont, *nextfont; Fnt *usedfont, *curfont, *nextfont;
size_t i, len; size_t i, len;
int utf8strlen, utf8charlen, render = x || y || w || h; int utf8strlen, utf8charlen, render;
long utf8codepoint = 0; long utf8codepoint = 0;
const char *utf8str; const char *utf8str;
FcCharSet *fccharset; FcCharSet *fccharset;
@ -252,19 +243,23 @@ drw_text(Drw *drw, int x, int y, unsigned int w, unsigned int h, unsigned int lp
XftResult result; XftResult result;
int charexists = 0; int charexists = 0;
if (!drw || (render && !drw->scheme) || !text || !drw->fonts) if (!(render = x || y || w || h)) {
return 0;
if (!render) {
w = ~w; w = ~w;
} else { }
XSetForeground(drw->dpy, drw->gc, drw->scheme[invert ? ColFg : ColBg].pixel);
if (!drw || !drw->scheme) {
return 0;
} else if (render) {
XSetForeground(drw->dpy, drw->gc, invert ? drw->scheme->fg.pix : drw->scheme->bg.pix);
XFillRectangle(drw->dpy, drw->drawable, drw->gc, x, y, w, h); XFillRectangle(drw->dpy, drw->drawable, drw->gc, x, y, w, h);
d = XftDrawCreate(drw->dpy, drw->drawable, }
DefaultVisual(drw->dpy, drw->screen),
DefaultColormap(drw->dpy, drw->screen)); if (!text || !drw->fonts) {
x += lpad; return 0;
w -= lpad; } else if (render) {
cmap = DefaultColormap(drw->dpy, drw->screen);
vis = DefaultVisual(drw->dpy, drw->screen);
d = XftDrawCreate(drw->dpy, drw->drawable, vis, cmap);
} }
usedfont = drw->fonts; usedfont = drw->fonts;
@ -287,30 +282,32 @@ drw_text(Drw *drw, int x, int y, unsigned int w, unsigned int h, unsigned int lp
} }
} }
if (!charexists || nextfont) if (!charexists || nextfont) {
break; break;
else } else {
charexists = 0; charexists = 0;
}
} }
if (utf8strlen) { if (utf8strlen) {
drw_font_getexts(usedfont, utf8str, utf8strlen, &ew, NULL); drw_font_getexts(usedfont, utf8str, utf8strlen, &ew, NULL);
/* shorten text if necessary */ /* shorten text if necessary */
for (len = MIN(utf8strlen, sizeof(buf) - 1); len && ew > w; len--) for(len = MIN(utf8strlen, (sizeof buf) - 1); len && (ew > w - drw->fonts->h || w < drw->fonts->h); len--)
drw_font_getexts(usedfont, utf8str, len, &ew, NULL); drw_font_getexts(usedfont, utf8str, len, &ew, NULL);
if (len) { if (len) {
memcpy(buf, utf8str, len); memcpy(buf, utf8str, len);
buf[len] = '\0'; buf[len] = '\0';
if (len < utf8strlen) if(len < utf8strlen)
for (i = len; i && i > len - 3; buf[--i] = '.') for(i = len; i && i > len - 3; buf[--i] = '.');
; /* NOP */
if (render) { if (render) {
ty = y + (h - usedfont->h) / 2 + usedfont->xfont->ascent; th = usedfont->ascent + usedfont->descent;
XftDrawStringUtf8(d, &drw->scheme[invert ? ColBg : ColFg], ty = y + (h / 2) - (th / 2) + usedfont->ascent;
usedfont->xfont, x, ty, (XftChar8 *)buf, len); tx = x + (h / 2);
XftDrawStringUtf8(d, invert ? &drw->scheme->bg.rgb : &drw->scheme->fg.rgb, usedfont->xfont, tx, ty, (XftChar8 *)buf, len);
} }
x += ew; x += ew;
w -= ew; w -= ew;
} }
@ -323,15 +320,18 @@ drw_text(Drw *drw, int x, int y, unsigned int w, unsigned int h, unsigned int lp
usedfont = nextfont; usedfont = nextfont;
} else { } else {
/* Regardless of whether or not a fallback font is found, the /* Regardless of whether or not a fallback font is found, the
* character must be drawn. */ * character must be drawn.
*/
charexists = 1; charexists = 1;
fccharset = FcCharSetCreate(); fccharset = FcCharSetCreate();
FcCharSetAddChar(fccharset, utf8codepoint); FcCharSetAddChar(fccharset, utf8codepoint);
if (!drw->fonts->pattern) { if (!drw->fonts->pattern) {
/* Refer to the comment in xfont_create for more information. */ /* Refer to the comment in drw_font_xcreate for more
die("the first font in the cache must be loaded from a font string."); * information.
*/
die("The first font in the cache must be loaded from a font string.\n");
} }
fcpattern = FcPatternDuplicate(drw->fonts->pattern); fcpattern = FcPatternDuplicate(drw->fonts->pattern);
@ -346,50 +346,47 @@ drw_text(Drw *drw, int x, int y, unsigned int w, unsigned int h, unsigned int lp
FcPatternDestroy(fcpattern); FcPatternDestroy(fcpattern);
if (match) { if (match) {
usedfont = xfont_create(drw, NULL, match); usedfont = drw_font_xcreate(drw, NULL, match);
if (usedfont && XftCharExists(drw->dpy, usedfont->xfont, utf8codepoint)) { if (usedfont && XftCharExists(drw->dpy, usedfont->xfont, utf8codepoint)) {
for (curfont = drw->fonts; curfont->next; curfont = curfont->next) for (curfont = drw->fonts; curfont->next; curfont = curfont->next)
; /* NOP */ ; /* just find the end of the linked list */
curfont->next = usedfont; curfont->next = usedfont;
} else { } else {
xfont_free(usedfont); drw_xfont_free(usedfont);
usedfont = drw->fonts; usedfont = drw->fonts;
} }
} }
} }
} }
if (d)
XftDrawDestroy(d);
return x + (render ? w : 0); if (render) {
XftDrawDestroy(d);
}
return x;
} }
void void
drw_map(Drw *drw, Window win, int x, int y, unsigned int w, unsigned int h) drw_map(Drw *drw, Window win, int x, int y, unsigned int w, unsigned int h) {
{ if(!drw)
if (!drw)
return; return;
XCopyArea(drw->dpy, drw->drawable, win, drw->gc, x, y, w, h, x, y); XCopyArea(drw->dpy, drw->drawable, win, drw->gc, x, y, w, h, x, y);
XSync(drw->dpy, False); XSync(drw->dpy, False);
} }
unsigned int unsigned int
drw_fontset_getwidth(Drw *drw, const char *text) drw_fontset_getwidth(Drw *drw, const char *text) {
{
if (!drw || !drw->fonts || !text) if (!drw || !drw->fonts || !text)
return 0; return 0;
return drw_text(drw, 0, 0, 0, 0, 0, text, 0); return drw_text(drw, 0, 0, 0, 0, text, 0);
} }
void void
drw_font_getexts(Fnt *font, const char *text, unsigned int len, unsigned int *w, unsigned int *h) drw_font_getexts(Fnt *font, const char *text, unsigned int len, unsigned int *w, unsigned int *h) {
{
XGlyphInfo ext; XGlyphInfo ext;
if (!font || !text) if(!font || !text)
return; return;
XftTextExtentsUtf8(font->dpy, font->xfont, (XftChar8 *)text, len, &ext); XftTextExtentsUtf8(font->dpy, font->xfont, (XftChar8 *)text, len, &ext);
if (w) if (w)
*w = ext.xOff; *w = ext.xOff;
@ -398,24 +395,19 @@ drw_font_getexts(Fnt *font, const char *text, unsigned int len, unsigned int *w,
} }
Cur * Cur *
drw_cur_create(Drw *drw, int shape) drw_cur_create(Drw *drw, int shape) {
{ Cur *cur = (Cur *)calloc(1, sizeof(Cur));
Cur *cur;
if (!drw || !(cur = ecalloc(1, sizeof(Cur)))) if(!drw || !cur)
return NULL; return NULL;
cur->cursor = XCreateFontCursor(drw->dpy, shape); cur->cursor = XCreateFontCursor(drw->dpy, shape);
return cur; return cur;
} }
void void
drw_cur_free(Drw *drw, Cur *cursor) drw_cur_free(Drw *drw, Cur *cursor) {
{ if(!drw || !cursor)
if (!cursor)
return; return;
XFreeCursor(drw->dpy, cursor->cursor); XFreeCursor(drw->dpy, cursor->cursor);
free(cursor); free(cursor);
} }

28
drw.h
View File

@ -1,19 +1,27 @@
/* See LICENSE file for copyright and license details. */ /* See LICENSE file for copyright and license details. */
#define DRW_FONT_CACHE_SIZE 32
typedef struct { typedef struct {
Cursor cursor; Cursor cursor;
} Cur; } Cur;
typedef struct Fnt { typedef struct Fnt Fnt;
struct Fnt {
Display *dpy; Display *dpy;
int ascent;
int descent;
unsigned int h; unsigned int h;
XftFont *xfont; XftFont *xfont;
FcPattern *pattern; FcPattern *pattern;
struct Fnt *next; Fnt *next;
} Fnt; };
enum { ColFg, ColBg }; /* Clr scheme index */ typedef struct {
typedef XftColor Clr; struct {
unsigned long pix;
XftColor rgb;
} fg, bg;
} Scm;
typedef struct { typedef struct {
unsigned int w, h; unsigned int w, h;
@ -22,7 +30,7 @@ typedef struct {
Window root; Window root;
Drawable drawable; Drawable drawable;
GC gc; GC gc;
Clr *scheme; Scm *scheme;
Fnt *fonts; Fnt *fonts;
} Drw; } Drw;
@ -38,8 +46,8 @@ unsigned int drw_fontset_getwidth(Drw *drw, const char *text);
void drw_font_getexts(Fnt *font, const char *text, unsigned int len, unsigned int *w, unsigned int *h); void drw_font_getexts(Fnt *font, const char *text, unsigned int len, unsigned int *w, unsigned int *h);
/* Colorscheme abstraction */ /* Colorscheme abstraction */
void drw_clr_create(Drw *drw, Clr *dest, const char *clrname); Scm *drw_scm_create(Drw *drw, const char *fgname, const char *bgname);
Clr *drw_scm_create(Drw *drw, const char *clrnames[], size_t clrcount); void drw_scm_free(Scm *scm);
/* Cursor abstraction */ /* Cursor abstraction */
Cur *drw_cur_create(Drw *drw, int shape); Cur *drw_cur_create(Drw *drw, int shape);
@ -47,11 +55,11 @@ void drw_cur_free(Drw *drw, Cur *cursor);
/* Drawing context manipulation */ /* Drawing context manipulation */
void drw_setfontset(Drw *drw, Fnt *set); void drw_setfontset(Drw *drw, Fnt *set);
void drw_setscheme(Drw *drw, Clr *scm); void drw_setscheme(Drw *drw, Scm *scm);
/* Drawing functions */ /* Drawing functions */
void drw_rect(Drw *drw, int x, int y, unsigned int w, unsigned int h, int filled, int invert); void drw_rect(Drw *drw, int x, int y, unsigned int w, unsigned int h, int filled, int invert);
int drw_text(Drw *drw, int x, int y, unsigned int w, unsigned int h, unsigned int lpad, const char *text, int invert); int drw_text(Drw *drw, int x, int y, unsigned int w, unsigned int h, const char *text, int invert);
/* Map functions */ /* Map functions */
void drw_map(Drw *drw, Window win, int x, int y, unsigned int w, unsigned int h); void drw_map(Drw *drw, Window win, int x, int y, unsigned int w, unsigned int h);

19
example
View File

@ -20,8 +20,7 @@ easy to use
depends on depends on
♽ Xlib ♽ Xlib
☢ Xft ☢ libpng
☃ farbfeld
~1000 lines of code ~1000 lines of code
@ -30,8 +29,8 @@ $ sent FILE1 [FILE2 …]
▸ one slide per paragraph ▸ one slide per paragraph
▸ lines starting with # are ignored ▸ lines starting with # are ignored
image slide: paragraph containing @FILENAME paragraphs starting with a @ line are png images
▸ empty slide: just use a \ as a paragraph for an empty slide just use a \ as a paragraph
# This is a comment and will not be part of the presentation # This is a comment and will not be part of the presentation
@ -50,20 +49,10 @@ $ sent FILE1 [FILE2 …]
\#This line as well \#This line as well
⇒ Prepend a backslash to kill behaviour of special characters ⇒ Prepend a backslash to kill behaviour of special characters
Images are handled in the
http://tools.suckless.org/farbfeld/
format internally.
sent also supports transparent images.
Try changing the background in config.h
and rebuild.
@transparent_test.ff
😀😁😂😃😄😅😆😇😈😉😊😋😌😍😎😏 😀😁😂😃😄😅😆😇😈😉😊😋😌😍😎😏
😐😑😒😓😔😕😖😗😘😙😚😛😜😝😞😟 😐😑😒😓😔😕😖😗😘😙😚😛😜😝😞😟
😠😡😢😣😥😦😧😨😩😪😫😭😮😯😰😱 😠😡😢😣😥😦😧😨😩😪😫😭😮😯😰😱
😲😳😴😵😶😷😸😹😺😻😼😽😾😿🙀☠ 😲😳😴😵😶😷😸😹😺😻😼😽😾😿🙀☠
thanks. thanks
questions? questions?

72
sent.1
View File

@ -1,72 +0,0 @@
.Dd August 12, 2016
.Dt SENT 1
.Os
.Sh NAME
.Nm sent
.Nd simple plaintext presentation tool
.Sh SYNOPSIS
.Nm
.Op Fl v
.Op Ar file
.Sh DESCRIPTION
.Nm
is a simple plain text presentation tool for X. sent does not need LaTeX,
LibreOffice or any other fancy file format.
Instead, sent reads plain text describing the slides. sent can also draw
images.
.Pp
Every paragraph represents a slide in the presentation.
Especially for presentations using the Takahashi method this is very nice and
allows you to write the presentation for a quick lightning talk within a few
minutes.
.Sh OPTIONS
.Bl -tag -width Ds
.It Fl v
Print version information to stdout and exit.
.El
.Sh USAGE
.Bl -tag -width Ds
.It Em Mouse commands
.Bl -tag -width Ds
.It Sy Button1 | Button5
Go to next slide, if existent.
.It Sy Button3 | Button4
Go to previous slide, if existent.
.El
.It Em Keyboard commands
.Bl -tag -width Ds
.It Sy Escape | q
Quit.
.It Sy r
Reload the slides.
Only works on file input.
.It Sy Right | Return | Space | l | j | Down | Next | n
Go to next slide, if existent.
.It Sy Left | Backspace | h | k | Up | Prior | p
Go to previous slide, if existent.
.El
.El
.Sh FORMAT
The presentation file is made up of at least one paragraph, with an
empty line separating two slides.
Each input line is interpreted literally, except from control characters
at the beginning of lines described as follows:
.Bl -tag -width Ds
.It Sy @
Create individual slide containing the image pointed to by the filename
following the
.Sy @ .
.It Sy #
Ignore this input line.
.It Sy \e
Create input line using the characters following the
.Sy \e
without interpreting them.
.El
.Sh CUSTOMIZATION
.Nm
can be customized by creating a custom config.h and (re)compiling the
source code.
This keeps it fast, secure and simple.
.Sh SEE ALSO
.Xr 2ff 1

543
sent.c
View File

@ -1,17 +1,12 @@
/* See LICENSE file for copyright and license details. */ /* See LICENSE for licence details. */
#include <sys/types.h>
#include <arpa/inet.h>
#include <errno.h> #include <errno.h>
#include <fcntl.h>
#include <math.h> #include <math.h>
#include <regex.h> #include <png.h>
#include <stdarg.h> #include <stdarg.h>
#include <stdio.h> #include <stdio.h>
#include <stdint.h> #include <stdint.h>
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
#include <unistd.h>
#include <X11/keysym.h> #include <X11/keysym.h>
#include <X11/XKBlib.h> #include <X11/XKBlib.h>
#include <X11/Xatom.h> #include <X11/Xatom.h>
@ -20,7 +15,6 @@
#include <X11/Xft/Xft.h> #include <X11/Xft/Xft.h>
#include "arg.h" #include "arg.h"
#include "util.h"
#include "drw.h" #include "drw.h"
char *argv0; char *argv0;
@ -32,7 +26,9 @@ char *argv0;
typedef enum { typedef enum {
NONE = 0, NONE = 0,
SCALED = 1, LOADED = 1,
SCALED = 2,
DRAWN = 4
} imgstate; } imgstate;
typedef struct { typedef struct {
@ -40,19 +36,16 @@ typedef struct {
unsigned int bufwidth, bufheight; unsigned int bufwidth, bufheight;
imgstate state; imgstate state;
XImage *ximg; XImage *ximg;
FILE *f;
png_structp png_ptr;
png_infop info_ptr;
int numpasses; int numpasses;
} Image; } Image;
typedef struct {
char *regex;
char *bin;
} Filter;
typedef struct { typedef struct {
unsigned int linecount; unsigned int linecount;
char **lines; char **lines;
Image *img; Image *img;
char *embed;
} Slide; } Slide;
/* Purely graphic info */ /* Purely graphic info */
@ -86,25 +79,27 @@ typedef struct {
const Arg arg; const Arg arg;
} Shortcut; } Shortcut;
static void fffree(Image *img); static Image *pngopen(char *filename);
static void ffload(Slide *s); static void pngfree(Image *img);
static void ffprepare(Image *img); static int pngread(Image *img);
static void ffscale(Image *img); static int pngprepare(Image *img);
static void ffdraw(Image *img); static void pngscale(Image *img);
static void pngdraw(Image *img);
static void getfontsize(Slide *s, unsigned int *width, unsigned int *height); static void getfontsize(Slide *s, unsigned int *width, unsigned int *height);
static void cleanup(int slidesonly); static void cleanup();
static void reload(const Arg *arg); static void eprintf(const char *, ...);
static void die(const char *, ...);
static void load(FILE *fp); static void load(FILE *fp);
static void advance(const Arg *arg); static void advance(const Arg *arg);
static void quit(const Arg *arg); static void quit(const Arg *arg);
static void resize(int width, int height); static void resize(int width, int height);
static void run(void); static void run();
static void usage(void); static void usage();
static void xdraw(void); static void xdraw();
static void xhints(void); static void xhints();
static void xinit(void); static void xinit();
static void xloadfonts(void); static void xloadfonts();
static void bpress(XEvent *); static void bpress(XEvent *);
static void cmessage(XEvent *); static void cmessage(XEvent *);
@ -116,13 +111,12 @@ static void configure(XEvent *);
#include "config.h" #include "config.h"
/* Globals */ /* Globals */
static const char *fname = NULL;
static Slide *slides = NULL; static Slide *slides = NULL;
static int idx = 0; static int idx = 0;
static int slidecount = 0; static int slidecount = 0;
static XWindow xw; static XWindow xw;
static Drw *d = NULL; static Drw *d = NULL;
static Clr *sc; static Scm *sc;
static Fnt *fonts[NUMFONTSCALES]; static Fnt *fonts[NUMFONTSCALES];
static int running = 1; static int running = 1;
@ -134,128 +128,118 @@ static void (*handler[LASTEvent])(XEvent *) = {
[KeyPress] = kpress, [KeyPress] = kpress,
}; };
int Image *pngopen(char *filename)
filter(int fd, const char *cmd)
{ {
int fds[2]; FILE *f;
unsigned char buf[8];
Image *img;
if (pipe(fds) < 0) if (!(f = fopen(filename, "rb"))) {
die("sent: Unable to create pipe:"); eprintf("could not open file %s:", filename);
return NULL;
switch (fork()) {
case -1:
die("sent: Unable to fork:");
case 0:
dup2(fd, 0);
dup2(fds[1], 1);
close(fds[0]);
close(fds[1]);
execlp("sh", "sh", "-c", cmd, (char *)0);
fprintf(stderr, "sent: execlp sh -c '%s': %s\n", cmd, strerror(errno));
_exit(1);
} }
close(fds[1]);
return fds[0]; if (fread(buf, 1, 8, f) != 8 || png_sig_cmp(buf, 1, 8))
return NULL;
img = malloc(sizeof(Image));
memset(img, 0, sizeof(Image));
if (!(img->png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL,
NULL, NULL))) {
free(img);
return NULL;
}
if (!(img->info_ptr = png_create_info_struct(img->png_ptr))
|| setjmp(png_jmpbuf(img->png_ptr))) {
pngfree(img);
return NULL;
}
img->f = f;
rewind(f);
png_init_io(img->png_ptr, f);
png_read_info(img->png_ptr, img->info_ptr);
img->bufwidth = png_get_image_width(img->png_ptr, img->info_ptr);
img->bufheight = png_get_image_height(img->png_ptr, img->info_ptr);
return img;
} }
void void pngfree(Image *img)
fffree(Image *img)
{ {
png_destroy_read_struct(&img->png_ptr, img->info_ptr ? &img->info_ptr : NULL, NULL);
free(img->buf); free(img->buf);
if (img->ximg) if (img->ximg)
XDestroyImage(img->ximg); XDestroyImage(img->ximg);
free(img); free(img);
} }
void int pngread(Image *img)
ffload(Slide *s)
{ {
uint32_t y, x; unsigned int y;
uint16_t *row; png_bytepp row_pointers;
uint8_t opac, fg_r, fg_g, fg_b, bg_r, bg_g, bg_b;
size_t rowlen, off, nbytes, i;
ssize_t count;
unsigned char hdr[16];
char *bin = NULL;
char *filename;
regex_t regex;
int fdin, fdout;
if (s->img || !(filename = s->embed) || !s->embed[0]) if (!img)
return; /* already done */ return 0;
for (i = 0; i < LEN(filters); i++) { if (img->state & LOADED)
if (regcomp(&regex, filters[i].regex, return 2;
REG_NOSUB | REG_EXTENDED | REG_ICASE)) {
fprintf(stderr, "sent: Invalid regex '%s'\n", filters[i].regex);
continue;
}
if (!regexec(&regex, filename, 0, NULL, 0)) {
bin = filters[i].bin;
regfree(&regex);
break;
}
regfree(&regex);
}
if (!bin)
die("sent: Unable to find matching filter for '%s'", filename);
if ((fdin = open(filename, O_RDONLY)) < 0) if (img->buf)
die("sent: Unable to open '%s':", filename); free(img->buf);
if (!(img->buf = malloc(3 * img->bufwidth * img->bufheight)))
return 0;
if ((fdout = filter(fdin, bin)) < 0) if (setjmp(png_jmpbuf(img->png_ptr))) {
die("sent: Unable to filter '%s':", filename); png_destroy_read_struct(&img->png_ptr, &img->info_ptr, NULL);
close(fdin); return 0;
if (read(fdout, hdr, 16) != 16)
die("sent: Unable to read filtered file '%s':", filename);
if (memcmp("farbfeld", hdr, 8))
die("sent: Filtered file '%s' has no valid farbfeld header", filename);
s->img = ecalloc(1, sizeof(Image));
s->img->bufwidth = ntohl(*(uint32_t *)&hdr[8]);
s->img->bufheight = ntohl(*(uint32_t *)&hdr[12]);
free(s->img->buf);
/* internally the image is stored in 888 format */
s->img->buf = ecalloc(s->img->bufwidth * s->img->bufheight, strlen("888"));
/* scratch buffer to read row by row */
rowlen = s->img->bufwidth * 2 * strlen("RGBA");
row = ecalloc(1, rowlen);
/* extract window background color channels for transparency */
bg_r = (sc[ColBg].pixel >> 16) % 256;
bg_g = (sc[ColBg].pixel >> 8) % 256;
bg_b = (sc[ColBg].pixel >> 0) % 256;
for (off = 0, y = 0; y < s->img->bufheight; y++) {
nbytes = 0;
while (nbytes < rowlen) {
count = read(fdout, (char *)row + nbytes, rowlen - nbytes);
if (count < 0)
die("sent: Unable to read from pipe:");
nbytes += count;
}
for (x = 0; x < rowlen / 2; x += 4) {
fg_r = ntohs(row[x + 0]) / 257;
fg_g = ntohs(row[x + 1]) / 257;
fg_b = ntohs(row[x + 2]) / 257;
opac = ntohs(row[x + 3]) / 257;
/* blend opaque part of image data with window background color to
* emulate transparency */
s->img->buf[off++] = (fg_r * opac + bg_r * (255 - opac)) / 255;
s->img->buf[off++] = (fg_g * opac + bg_g * (255 - opac)) / 255;
s->img->buf[off++] = (fg_b * opac + bg_b * (255 - opac)) / 255;
}
} }
free(row); {
close(fdout); int color_type = png_get_color_type(img->png_ptr, img->info_ptr);
int bit_depth = png_get_bit_depth(img->png_ptr, img->info_ptr);
if (color_type == PNG_COLOR_TYPE_PALETTE)
png_set_expand(img->png_ptr);
if (color_type == PNG_COLOR_TYPE_GRAY && bit_depth < 8)
png_set_expand(img->png_ptr);
if (png_get_valid(img->png_ptr, img->info_ptr, PNG_INFO_tRNS))
png_set_expand(img->png_ptr);
if (bit_depth == 16)
png_set_strip_16(img->png_ptr);
if (color_type == PNG_COLOR_TYPE_GRAY
|| color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
png_set_gray_to_rgb(img->png_ptr);
png_color_16 my_background = {.red = 0xff, .green = 0xff, .blue = 0xff};
png_color_16p image_background;
if (png_get_bKGD(img->png_ptr, img->info_ptr, &image_background))
png_set_background(img->png_ptr, image_background, PNG_BACKGROUND_GAMMA_FILE, 1, 1.0);
else
png_set_background(img->png_ptr, &my_background, PNG_BACKGROUND_GAMMA_SCREEN, 2, 1.0);
if (png_get_interlace_type(img->png_ptr, img->info_ptr) == PNG_INTERLACE_ADAM7)
img->numpasses = png_set_interlace_handling(img->png_ptr);
else
img->numpasses = 1;
png_read_update_info(img->png_ptr, img->info_ptr);
}
row_pointers = (png_bytepp)malloc(img->bufheight * sizeof(png_bytep));
for (y = 0; y < img->bufheight; y++)
row_pointers[y] = img->buf + y * img->bufwidth * 3;
png_read_image(img->png_ptr, row_pointers);
free(row_pointers);
png_destroy_read_struct(&img->png_ptr, &img->info_ptr, NULL);
fclose(img->f);
img->state |= LOADED;
return 1;
} }
void int pngprepare(Image *img)
ffprepare(Image *img)
{ {
int depth = DefaultDepth(xw.dpy, xw.scr); int depth = DefaultDepth(xw.dpy, xw.scr);
int width = xw.uw; int width = xw.uw;
@ -266,26 +250,38 @@ ffprepare(Image *img)
else else
height = img->bufheight * xw.uw / img->bufwidth; height = img->bufheight * xw.uw / img->bufwidth;
if (depth < 24) if (depth < 24) {
die("sent: Display color depths < 24 not supported"); eprintf("display depths <24 not supported.");
return 0;
if (img->ximg) }
XDestroyImage(img->ximg);
if (!(img->ximg = XCreateImage(xw.dpy, CopyFromParent, depth, ZPixmap, 0, if (!(img->ximg = XCreateImage(xw.dpy, CopyFromParent, depth, ZPixmap, 0,
NULL, width, height, 32, 0))) NULL, width, height, 32, 0))) {
die("sent: Unable to create XImage"); eprintf("could not create XImage");
return 0;
}
img->ximg->data = ecalloc(height, img->ximg->bytes_per_line); if (!(img->ximg->data = malloc(img->ximg->bytes_per_line * height))) {
if (!XInitImage(img->ximg)) eprintf("could not alloc data section for XImage");
die("sent: Unable to initiate XImage"); XDestroyImage(img->ximg);
img->ximg = NULL;
return 0;
}
ffscale(img); if (!XInitImage(img->ximg)) {
eprintf("could not init XImage");
free(img->ximg->data);
XDestroyImage(img->ximg);
img->ximg = NULL;
return 0;
}
pngscale(img);
img->state |= SCALED; img->state |= SCALED;
return 1;
} }
void void pngscale(Image *img)
ffscale(Image *img)
{ {
unsigned int x, y; unsigned int x, y;
unsigned int width = img->ximg->width; unsigned int width = img->ximg->width;
@ -310,103 +306,104 @@ ffscale(Image *img)
} }
} }
void void pngdraw(Image *img)
ffdraw(Image *img)
{ {
int xoffset = (xw.w - img->ximg->width) / 2; int xoffset = (xw.w - img->ximg->width) / 2;
int yoffset = (xw.h - img->ximg->height) / 2; int yoffset = (xw.h - img->ximg->height) / 2;
XPutImage(xw.dpy, xw.win, d->gc, img->ximg, 0, 0, XPutImage(xw.dpy, xw.win, d->gc, img->ximg, 0, 0,
xoffset, yoffset, img->ximg->width, img->ximg->height); xoffset, yoffset, img->ximg->width, img->ximg->height);
XFlush(xw.dpy); XFlush(xw.dpy);
img->state |= DRAWN;
} }
void void getfontsize(Slide *s, unsigned int *width, unsigned int *height)
getfontsize(Slide *s, unsigned int *width, unsigned int *height)
{ {
int i, j; size_t i, j;
unsigned int curw, newmax; unsigned int curw, imax;
float lfac = linespacing * (s->linecount - 1) + 1; float lfac = linespacing * (s->linecount - 1) + 1;
/* fit height */ /* fit height */
for (j = NUMFONTSCALES - 1; j >= 0; j--) for (j = NUMFONTSCALES - 1; j >= 0; j--)
if (fonts[j]->h * lfac <= xw.uh) if (fonts[j]->h * lfac <= xw.uh)
break; break;
LIMIT(j, 0, NUMFONTSCALES - 1);
drw_setfontset(d, fonts[j]); drw_setfontset(d, fonts[j]);
/* fit width */ /* fit width */
*width = 0; *width = 0;
for (i = 0; i < s->linecount; i++) { for (i = 0; i < s->linecount; i++) {
curw = drw_fontset_getwidth(d, s->lines[i]); curw = drw_fontset_getwidth(d, s->lines[i]);
newmax = (curw >= *width); if (curw >= *width)
while (j > 0 && curw > xw.uw) { imax = i;
while (j >= 0 && curw > xw.uw) {
drw_setfontset(d, fonts[--j]); drw_setfontset(d, fonts[--j]);
curw = drw_fontset_getwidth(d, s->lines[i]); curw = drw_fontset_getwidth(d, s->lines[i]);
} }
if (newmax) if (imax == i)
*width = curw; *width = curw;
} }
*height = fonts[j]->h * lfac; *height = fonts[j]->h * lfac;
*width += fonts[j]->h;
} }
void void cleanup()
cleanup(int slidesonly)
{ {
unsigned int i, j; unsigned int i, j;
if (!slidesonly) { for (i = 0; i < NUMFONTSCALES; i++)
for (i = 0; i < NUMFONTSCALES; i++) drw_fontset_free(fonts[i]);
drw_fontset_free(fonts[i]); drw_scm_free(sc);
free(sc); drw_free(d);
drw_free(d);
XDestroyWindow(xw.dpy, xw.win);
XSync(xw.dpy, False);
XCloseDisplay(xw.dpy);
}
XDestroyWindow(xw.dpy, xw.win);
XSync(xw.dpy, False);
XCloseDisplay(xw.dpy);
if (slides) { if (slides) {
for (i = 0; i < slidecount; i++) { for (i = 0; i < slidecount; i++) {
for (j = 0; j < slides[i].linecount; j++) for (j = 0; j < slides[i].linecount; j++)
free(slides[i].lines[j]); free(slides[i].lines[j]);
free(slides[i].lines); free(slides[i].lines);
if (slides[i].img) if (slides[i].img)
fffree(slides[i].img); pngfree(slides[i].img);
}
if (!slidesonly) {
free(slides);
slides = NULL;
} }
free(slides);
slides = NULL;
} }
} }
void void die(const char *fmt, ...)
reload(const Arg *arg)
{ {
FILE *fp = NULL; va_list ap;
unsigned int i;
if (!fname) { va_start(ap, fmt);
fprintf(stderr, "sent: Cannot reload from stdin. Use a file!\n"); vfprintf(stderr, fmt, ap);
return; va_end(ap);
if (fmt[0] != '\0' && fmt[strlen(fmt)-1] == ':') {
fputc(' ', stderr);
perror(NULL);
} else {
fputc('\n', stderr);
} }
exit(1);
cleanup(1);
slidecount = 0;
if (!(fp = fopen(fname, "r")))
die("sent: Unable to open '%s' for reading:", fname);
load(fp);
fclose(fp);
LIMIT(idx, 0, slidecount-1);
for (i = 0; i < slidecount; i++)
ffload(&slides[i]);
xdraw();
} }
void void eprintf(const char *fmt, ...)
load(FILE *fp) {
va_list ap;
va_start(ap, fmt);
vfprintf(stderr, fmt, ap);
va_end(ap);
if (fmt[0] != '\0' && fmt[strlen(fmt)-1] == ':') {
fputc(' ', stderr);
perror(NULL);
} else {
fputc('\n', stderr);
}
}
void load(FILE *fp)
{ {
static size_t size = 0; static size_t size = 0;
size_t blen, maxlines; size_t blen, maxlines;
@ -415,6 +412,10 @@ load(FILE *fp)
/* read each line from fp and add it to the item list */ /* read each line from fp and add it to the item list */
while (1) { while (1) {
if ((slidecount+1) * sizeof(*slides) >= size)
if (!(slides = realloc(slides, (size += BUFSIZ))))
die("cannot realloc %u bytes:", size);
/* eat consecutive empty lines */ /* eat consecutive empty lines */
while ((p = fgets(buf, sizeof(buf), fp))) while ((p = fgets(buf, sizeof(buf), fp)))
if (strcmp(buf, "\n") != 0 && buf[0] != '#') if (strcmp(buf, "\n") != 0 && buf[0] != '#')
@ -422,18 +423,10 @@ load(FILE *fp)
if (!p) if (!p)
break; break;
if ((slidecount+1) * sizeof(*slides) >= size)
if (!(slides = realloc(slides, (size += BUFSIZ))))
die("sent: Unable to reallocate %u bytes:", size);
/* read one slide */ /* read one slide */
maxlines = 0; maxlines = 0;
memset((s = &slides[slidecount]), 0, sizeof(Slide)); memset((s = &slides[slidecount]), 0, sizeof(Slide));
do { do {
/* if there's a leading null, we can't do blen-1 */
if (buf[0] == '\0')
continue;
if (buf[0] == '#') if (buf[0] == '#')
continue; continue;
@ -441,54 +434,53 @@ load(FILE *fp)
if (s->linecount >= maxlines) { if (s->linecount >= maxlines) {
maxlines = 2 * s->linecount + 1; maxlines = 2 * s->linecount + 1;
if (!(s->lines = realloc(s->lines, maxlines * sizeof(s->lines[0])))) if (!(s->lines = realloc(s->lines, maxlines * sizeof(s->lines[0]))))
die("sent: Unable to reallocate %u bytes:", maxlines * sizeof(s->lines[0])); die("cannot realloc %u bytes:", maxlines * sizeof(s->lines[0]));
} }
blen = strlen(buf); blen = strlen(buf);
if (!(s->lines[s->linecount] = strdup(buf))) if (!(s->lines[s->linecount] = strdup(buf)))
die("sent: Unable to strdup:"); die("cannot strdup:");
if (s->lines[s->linecount][blen-1] == '\n') if (s->lines[s->linecount][blen-1] == '\n')
s->lines[s->linecount][blen-1] = '\0'; s->lines[s->linecount][blen-1] = '\0';
/* mark as image slide if first line of a slide starts with @ */ /* only make image slide if first line of a slide starts with @ */
if (s->linecount == 0 && s->lines[0][0] == '@') if (s->linecount == 0 && s->lines[0][0] == '@') {
s->embed = &s->lines[0][1]; memmove(s->lines[0], &s->lines[0][1], blen);
s->img = pngopen(s->lines[0]);
}
if (s->lines[s->linecount][0] == '\\') if (s->lines[s->linecount][0] == '\\')
memmove(s->lines[s->linecount], &s->lines[s->linecount][1], blen); memmove(s->lines[s->linecount], &s->lines[s->linecount][1], blen);
s->linecount++; s->linecount++;
} while ((p = fgets(buf, sizeof(buf), fp)) && strcmp(buf, "\n") != 0); } while ((p = fgets(buf, sizeof(buf), fp)) && strcmp(buf, "\n") != 0);
slidecount++; slidecount++;
if (!p) if (!p)
break; break;
} }
if (!slidecount)
die("sent: No slides in file");
} }
void void advance(const Arg *arg)
advance(const Arg *arg)
{ {
int new_idx = idx + arg->i; int new_idx = idx + arg->i;
LIMIT(new_idx, 0, slidecount-1); LIMIT(new_idx, 0, slidecount-1);
if (new_idx != idx) { if (new_idx != idx) {
if (slides[idx].img) if (slides[idx].img)
slides[idx].img->state &= ~SCALED; slides[idx].img->state &= ~(DRAWN | SCALED);
idx = new_idx; idx = new_idx;
xdraw(); xdraw();
if (slidecount > idx + 1 && slides[idx + 1].img && !pngread(slides[idx + 1].img))
die("could not read image %s", slides[idx + 1].lines[0]);
if (0 < idx && slides[idx - 1].img && !pngread(slides[idx - 1].img))
die("could not read image %s", slides[idx - 1].lines[0]);
} }
} }
void void quit(const Arg *arg)
quit(const Arg *arg)
{ {
running = 0; running = 0;
} }
void void resize(int width, int height)
resize(int width, int height)
{ {
xw.w = width; xw.w = width;
xw.h = height; xw.h = height;
@ -497,8 +489,7 @@ resize(int width, int height)
drw_resize(d, width, height); drw_resize(d, width, height);
} }
void void run()
run(void)
{ {
XEvent ev; XEvent ev;
@ -519,8 +510,13 @@ run(void)
} }
} }
void void usage()
xdraw(void) {
die("sent " VERSION " (c) 2014-2015 markus.teich@stusta.mhn.de\n" \
"usage: sent FILE1 [FILE2 ...]", argv0);
}
void xdraw()
{ {
unsigned int height, width, i; unsigned int height, width, i;
Image *im = slides[idx].img; Image *im = slides[idx].img;
@ -536,26 +532,26 @@ xdraw(void)
(xw.h - height) / 2 + i * linespacing * d->fonts->h, (xw.h - height) / 2 + i * linespacing * d->fonts->h,
width, width,
d->fonts->h, d->fonts->h,
0,
slides[idx].lines[i], slides[idx].lines[i],
0); 0);
drw_map(d, xw.win, 0, 0, xw.w, xw.h); drw_map(d, xw.win, 0, 0, xw.w, xw.h);
} else { } else if (!(im->state & LOADED) && !pngread(im)) {
if (!(im->state & SCALED)) eprintf("could not read image %s", slides[idx].lines[0]);
ffprepare(im); } else if (!(im->state & SCALED) && !pngprepare(im)) {
ffdraw(im); eprintf("could not prepare image %s for drawing", slides[idx].lines[0]);
} else if (!(im->state & DRAWN)) {
pngdraw(im);
} }
} }
void void xhints()
xhints(void)
{ {
XClassHint class = {.res_name = "sent", .res_class = "presenter"}; XClassHint class = {.res_name = "sent", .res_class = "presenter"};
XWMHints wm = {.flags = InputHint, .input = True}; XWMHints wm = {.flags = InputHint, .input = True};
XSizeHints *sizeh = NULL; XSizeHints *sizeh = NULL;
if (!(sizeh = XAllocSizeHints())) if (!(sizeh = XAllocSizeHints()))
die("sent: Unable to allocate size hints"); die("sent: Could not alloc size hints");
sizeh->flags = PSize; sizeh->flags = PSize;
sizeh->height = xw.h; sizeh->height = xw.h;
@ -565,40 +561,35 @@ xhints(void)
XFree(sizeh); XFree(sizeh);
} }
void void xinit()
xinit(void)
{ {
XTextProperty prop; XTextProperty prop;
unsigned int i;
if (!(xw.dpy = XOpenDisplay(NULL))) if (!(xw.dpy = XOpenDisplay(NULL)))
die("sent: Unable to open display"); die("Can't open display.");
xw.scr = XDefaultScreen(xw.dpy); xw.scr = XDefaultScreen(xw.dpy);
xw.vis = XDefaultVisual(xw.dpy, xw.scr); xw.vis = XDefaultVisual(xw.dpy, xw.scr);
resize(DisplayWidth(xw.dpy, xw.scr), DisplayHeight(xw.dpy, xw.scr)); resize(DisplayWidth(xw.dpy, xw.scr), DisplayHeight(xw.dpy, xw.scr));
xw.attrs.background_pixel = WhitePixel(xw.dpy, xw.scr);
xw.attrs.bit_gravity = CenterGravity; xw.attrs.bit_gravity = CenterGravity;
xw.attrs.event_mask = KeyPressMask | ExposureMask | StructureNotifyMask | xw.attrs.event_mask = KeyPressMask | ExposureMask | StructureNotifyMask
ButtonMotionMask | ButtonPressMask; | ButtonMotionMask | ButtonPressMask;
xw.win = XCreateWindow(xw.dpy, XRootWindow(xw.dpy, xw.scr), 0, 0, xw.win = XCreateWindow(xw.dpy, XRootWindow(xw.dpy, xw.scr), 0, 0,
xw.w, xw.h, 0, XDefaultDepth(xw.dpy, xw.scr), xw.w, xw.h, 0, XDefaultDepth(xw.dpy, xw.scr), InputOutput, xw.vis,
InputOutput, xw.vis, CWBitGravity | CWEventMask, CWBackPixel | CWBitGravity | CWEventMask, &xw.attrs);
&xw.attrs);
xw.wmdeletewin = XInternAtom(xw.dpy, "WM_DELETE_WINDOW", False); xw.wmdeletewin = XInternAtom(xw.dpy, "WM_DELETE_WINDOW", False);
xw.netwmname = XInternAtom(xw.dpy, "_NET_WM_NAME", False); xw.netwmname = XInternAtom(xw.dpy, "_NET_WM_NAME", False);
XSetWMProtocols(xw.dpy, xw.win, &xw.wmdeletewin, 1); XSetWMProtocols(xw.dpy, xw.win, &xw.wmdeletewin, 1);
if (!(d = drw_create(xw.dpy, xw.scr, xw.win, xw.w, xw.h))) if (!(d = drw_create(xw.dpy, xw.scr, xw.win, xw.w, xw.h)))
die("sent: Unable to create drawing context"); die("Can't create drawing context.");
sc = drw_scm_create(d, colors, 2); sc = drw_scm_create(d, fgcol, bgcol);
drw_setscheme(d, sc); drw_setscheme(d, sc);
XSetWindowBackground(xw.dpy, xw.win, sc[ColBg].pixel);
xloadfonts(); xloadfonts();
for (i = 0; i < slidecount; i++)
ffload(&slides[i]);
XStringListToTextProperty(&argv0, 1, &prop); XStringListToTextProperty(&argv0, 1, &prop);
XSetWMName(xw.dpy, xw.win, &prop); XSetWMName(xw.dpy, xw.win, &prop);
@ -609,31 +600,30 @@ xinit(void)
XSync(xw.dpy, False); XSync(xw.dpy, False);
} }
void void xloadfonts()
xloadfonts(void)
{ {
int i, j; int i, j;
char *fstrs[LEN(fontfallbacks)]; char *fstrs[LEN(fontfallbacks)];
for (j = 0; j < LEN(fontfallbacks); j++) { for (j = 0; j < LEN(fontfallbacks); j++) {
fstrs[j] = ecalloc(1, MAXFONTSTRLEN); if (!(fstrs[j] = malloc(MAXFONTSTRLEN)))
die("could not malloc fstrs");
} }
for (i = 0; i < NUMFONTSCALES; i++) { for (i = 0; i < NUMFONTSCALES; i++) {
for (j = 0; j < LEN(fontfallbacks); j++) { for (j = 0; j < LEN(fontfallbacks); j++) {
if (MAXFONTSTRLEN < snprintf(fstrs[j], MAXFONTSTRLEN, "%s:size=%d", fontfallbacks[j], FONTSZ(i))) if (MAXFONTSTRLEN < snprintf(fstrs[j], MAXFONTSTRLEN, "%s:size=%d", fontfallbacks[j], FONTSZ(i)))
die("sent: Font string too long"); die("font string too long");
} }
if (!(fonts[i] = drw_fontset_create(d, (const char**)fstrs, LEN(fstrs)))) fonts[i] = drw_fontset_create(d, (const char**)fstrs, LEN(fstrs));
die("sent: Unable to load any font for size %d", FONTSZ(i));
} }
for (j = 0; j < LEN(fontfallbacks); j++) for (j = 0; j < LEN(fontfallbacks); j++)
free(fstrs[j]); if (fstrs[j])
free(fstrs[j]);
} }
void void bpress(XEvent *e)
bpress(XEvent *e)
{ {
unsigned int i; unsigned int i;
@ -642,22 +632,19 @@ bpress(XEvent *e)
mshortcuts[i].func(&(mshortcuts[i].arg)); mshortcuts[i].func(&(mshortcuts[i].arg));
} }
void void cmessage(XEvent *e)
cmessage(XEvent *e)
{ {
if (e->xclient.data.l[0] == xw.wmdeletewin) if (e->xclient.data.l[0] == xw.wmdeletewin)
running = 0; running = 0;
} }
void void expose(XEvent *e)
expose(XEvent *e)
{ {
if (0 == e->xexpose.count) if (0 == e->xexpose.count)
xdraw(); xdraw();
} }
void void kpress(XEvent *e)
kpress(XEvent *e)
{ {
unsigned int i; unsigned int i;
KeySym sym; KeySym sym;
@ -668,44 +655,40 @@ kpress(XEvent *e)
shortcuts[i].func(&(shortcuts[i].arg)); shortcuts[i].func(&(shortcuts[i].arg));
} }
void void configure(XEvent *e)
configure(XEvent *e)
{ {
resize(e->xconfigure.width, e->xconfigure.height); resize(e->xconfigure.width, e->xconfigure.height);
if (slides[idx].img) if (slides[idx].img)
slides[idx].img->state &= ~SCALED; slides[idx].img->state &= ~(DRAWN | SCALED);
xdraw(); xdraw();
} }
void int main(int argc, char *argv[])
usage(void)
{
die("usage: %s [file]", argv0);
}
int
main(int argc, char *argv[])
{ {
int i;
FILE *fp = NULL; FILE *fp = NULL;
ARGBEGIN { ARGBEGIN {
case 'v': case 'v':
fprintf(stderr, "sent-"VERSION"\n");
return 0;
default: default:
usage(); usage();
} ARGEND } ARGEND;
if (!argv[0] || !strcmp(argv[0], "-")) for (i = 0; i < argc; i++) {
fp = stdin; if ((fp = strcmp(argv[i], "-") ? fopen(argv[i], "r") : stdin)) {
else if (!(fp = fopen(fname = argv[0], "r"))) load(fp);
die("sent: Unable to open '%s' for reading:", fname); fclose(fp);
load(fp); } else {
fclose(fp); eprintf("could not open %s for reading:", argv[i]);
}
}
if (!slides || !slides[0].lines)
usage();
xinit(); xinit();
run(); run();
cleanup(0); cleanup();
return 0; return 0;
} }

Binary file not shown.

26
util.c
View File

@ -2,34 +2,16 @@
#include <stdarg.h> #include <stdarg.h>
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
#include <string.h>
#include "util.h" #include "util.h"
void *
ecalloc(size_t nmemb, size_t size)
{
void *p;
if (!(p = calloc(nmemb, size)))
die("calloc:");
return p;
}
void void
die(const char *fmt, ...) { die(const char *errstr, ...) {
va_list ap; va_list ap;
va_start(ap, fmt); va_start(ap, errstr);
vfprintf(stderr, fmt, ap); vfprintf(stderr, errstr, ap);
va_end(ap); va_end(ap);
if (fmt[0] && fmt[strlen(fmt)-1] == ':') {
fputc(' ', stderr);
perror(NULL);
} else {
fputc('\n', stderr);
}
exit(1); exit(1);
} }

3
util.h
View File

@ -4,5 +4,4 @@
#define MIN(A, B) ((A) < (B) ? (A) : (B)) #define MIN(A, B) ((A) < (B) ? (A) : (B))
#define BETWEEN(X, A, B) ((A) <= (X) && (X) <= (B)) #define BETWEEN(X, A, B) ((A) <= (X) && (X) <= (B))
void die(const char *fmt, ...); void die(const char *errstr, ...);
void *ecalloc(size_t nmemb, size_t size);