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

View File

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

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
plaintext files to describe the slides and can include images via farbfeld.
Every paragraph represents a slide in the presentation.
plaintext files and png images. Every line represents a slide in the
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
is automatically scaled to fit the window and centered so you also 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.
The presentation is displayed in a simple X11 window colored black on white for
maximum contrast even if the sun shines directly onto the projected image. The
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.
Demo
----
To get a little demo, just type
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
-----
sent [FILE]
sent [-f FONTSTRING] FILE1 [FILE2 ...]
If FILE is omitted or equals `-`, stdin will be read. Produce image slides by
prepending a `@` in front of the filename as a single paragraph. Lines starting
with `#` will be ignored. A `\` at the beginning of the line escapes `@` and
`#`. A presentation file could look like this:
If one FILE equals `-`, stdin will be read. Use png images by prepending a `@`
before the filename. Lines starting with `#` will be ignored. A presentation
file could look like this:
sent
why?
@nyan.png
depends on
- Xlib
- Xft
- farbfeld
easy to use
depends on Xlib, libpng
no bloat
how?
sent FILENAME
one slide per paragraph
one slide per line
# This is a comment and will not be part of the presentation
\# This and the next line start with backslashes
\@FILE.png
# The next line starts with a whitespace, it will not produce an image slide
@FILE.png
thanks / questions?
future features
---------------
Development
sent is developed at http://tools.suckless.org/sent
0: http://tools.suckless.org/farbfeld/
* multiple lines per slide?
* light colored background and table of contents
* second window for speakers laptop (progress, time, notes?)
* markdown?

96
arg.h
View File

@ -1,49 +1,63 @@
/*
* ISC-License
*
* 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.
* Copy me if you can.
* by 20h
*/
#ifndef ARG_H
#define ARG_H
#ifndef ARG_H__
#define ARG_H__
extern char *argv0;
/* int main(int argc, char *argv[]) */
#define ARGBEGIN for (argv0 = *argv, *argv ? (argc--, argv++) : ((void *)0); \
*argv && (*argv)[0] == '-' && (*argv)[1]; argc--, argv++) { \
int i_, argused_; \
if ((*argv)[1] == '-' && !(*argv)[2]) { \
argc--, argv++; \
break; \
} \
for (i_ = 1, argused_ = 0; (*argv)[i_]; i_++) { \
switch((*argv)[i_])
#define ARGEND if (argused_) { \
if ((*argv)[i_ + 1]) { \
break; \
} else { \
argc--, argv++; \
break; \
} \
} \
} \
/* use main(int argc, char *argv[]) */
#define ARGBEGIN for (argv0 = *argv, argv++, argc--;\
argv[0] && argv[0][1]\
&& argv[0][0] == '-';\
argc--, argv++) {\
char argc_;\
char **argv_;\
int brk_;\
if (argv[0][1] == '-' && argv[0][2] == '\0') {\
argv++;\
argc--;\
break;\
}\
for (brk_ = 0, argv[0]++, argv_ = argv;\
argv[0][0] && !brk_;\
argv[0]++) {\
if (argv_ != argv)\
break;\
argc_ = argv[0][0];\
switch (argc_)
/* Handles obsolete -NUM syntax */
#define ARGNUM case '0':\
case '1':\
case '2':\
case '3':\
case '4':\
case '5':\
case '6':\
case '7':\
case '8':\
case '9'
#define ARGEND }\
}
#define ARGC() ((*argv)[i_])
#define ARGF_(x) (((*argv)[i_ + 1]) ? (argused_ = 1, &((*argv)[i_ + 1])) : \
(*(argv + 1)) ? (argused_ = 1, *(argv + 1)) : (x))
#define EARGF(x) ARGF_(((x), exit(1), (char *)0))
#define ARGF() ARGF_((char *)0)
#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

View File

@ -1,17 +1,15 @@
/* See LICENSE file for copyright and license details. */
static char *fontfallbacks[] = {
"dejavu sans",
"dejavu",
"roboto",
"ubuntu",
};
#define NUMFONTSCALES 42
#define NUMFONTSCALES 30
#define FONTSZ(x) ((int)(10.0 * powf(1.1288, (x)))) /* x in [0, NUMFONTSCALES-1] */
static const char *colors[] = {
"#000000", /* foreground color */
"#FFFFFF", /* background color */
};
static const char *fgcol = "#000000";
static const char *bgcol = "#FFFFFF";
static const float linespacing = 1.4;
@ -22,19 +20,15 @@ static const float usableheight = 0.75;
static Mousekey mshortcuts[] = {
/* button function argument */
{ Button1, advance, {.i = +1} },
{ Button3, advance, {.i = -1} },
{ Button4, advance, {.i = -1} },
{ Button5, advance, {.i = +1} },
{ Button2, advance, {.i = -1} },
};
static Shortcut shortcuts[] = {
/* keysym function argument */
{ XK_Escape, quit, {0} },
{ XK_q, quit, {0} },
{ XK_Right, advance, {.i = +1} },
{ XK_Left, advance, {.i = -1} },
{ XK_Return, advance, {.i = +1} },
{ XK_space, advance, {.i = +1} },
{ XK_BackSpace, advance, {.i = -1} },
{ XK_l, advance, {.i = +1} },
{ XK_h, advance, {.i = -1} },
@ -44,13 +38,4 @@ static Shortcut shortcuts[] = {
{ XK_Up, advance, {.i = -1} },
{ XK_Next, 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
VERSION = 1
VERSION = 0.1
# Customize below to fit your system
@ -12,12 +12,7 @@ X11LIB = /usr/X11R6/lib
# includes and libs
INCS = -I. -I/usr/include -I/usr/include/freetype2 -I${X11INC}
LIBS = -L/usr/lib -lc -lm -L${X11LIB} -lXft -lfontconfig -lX11
# 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
LIBS = -L/usr/lib -lc -lm -L${X11LIB} -lXft -lfontconfig -lX11 -lpng
# flags
CPPFLAGS = -DVERSION=\"${VERSION}\" -D_XOPEN_SOURCE=600

324
drw.c
View File

@ -11,60 +11,58 @@
#define UTF_INVALID 0xFFFD
#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 utfmask[UTF_SIZ + 1] = {0xC0, 0x80, 0xE0, 0xF0, 0xF8};
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 long
utf8decodebyte(const char c, size_t *i)
{
for (*i = 0; *i < (UTF_SIZ + 1); ++(*i))
if (((unsigned char)c & utfmask[*i]) == utfbyte[*i])
utf8decodebyte(const char c, size_t *i) {
for(*i = 0; *i < (UTF_SIZ + 1); ++(*i))
if(((unsigned char)c & utfmask[*i]) == utfbyte[*i])
return (unsigned char)c & ~utfmask[*i];
return 0;
}
static size_t
utf8validate(long *u, size_t i)
{
if (!BETWEEN(*u, utfmin[i], utfmax[i]) || BETWEEN(*u, 0xD800, 0xDFFF))
utf8validate(long *u, size_t i) {
if(!BETWEEN(*u, utfmin[i], utfmax[i]) || BETWEEN(*u, 0xD800, 0xDFFF))
*u = UTF_INVALID;
for (i = 1; *u > utfmax[i]; ++i)
for(i = 1; *u > utfmax[i]; ++i)
;
return i;
}
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;
long udecoded;
*u = UTF_INVALID;
if (!clen)
if(!clen)
return 0;
udecoded = utf8decodebyte(c[0], &len);
if (!BETWEEN(len, 1, UTF_SIZ))
if(!BETWEEN(len, 1, UTF_SIZ))
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);
if (type)
if(type != 0)
return j;
}
if (j < len)
if(j < len)
return 0;
*u = udecoded;
utf8validate(u, len);
return len;
}
Drw *
drw_create(Display *dpy, int screen, Window root, unsigned int w, unsigned int h)
{
Drw *drw = ecalloc(1, sizeof(Drw));
drw_create(Display *dpy, int screen, Window root, unsigned int w, unsigned int h) {
Drw *drw = (Drw *)calloc(1, sizeof(Drw));
if(!drw)
return NULL;
drw->dpy = dpy;
drw->screen = screen;
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->gc = XCreateGC(dpy, root, 0, NULL);
XSetLineAttributes(dpy, drw->gc, 1, LineSolid, CapButt, JoinMiter);
return drw;
}
void
drw_resize(Drw *drw, unsigned int w, unsigned int h)
{
if (!drw)
drw_resize(Drw *drw, unsigned int w, unsigned int h) {
if(!drw)
return;
drw->w = w;
drw->h = h;
if (drw->drawable)
if(drw->drawable != 0)
XFreePixmap(drw->dpy, drw->drawable);
drw->drawable = XCreatePixmap(drw->dpy, drw->root, w, h, DefaultDepth(drw->dpy, drw->screen));
}
void
drw_free(Drw *drw)
{
drw_free(Drw *drw) {
XFreePixmap(drw->dpy, drw->drawable);
XFreeGC(drw->dpy, drw->gc);
free(drw);
@ -102,123 +96,119 @@ drw_free(Drw *drw)
* drw_fontset_create instead.
*/
static Fnt *
xfont_create(Drw *drw, const char *fontname, FcPattern *fontpattern)
{
drw_font_xcreate(Drw *drw, const char *fontname, FcPattern *fontpattern) {
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) {
/* Using the pattern found at font->xfont->pattern does not yield the
* same substitution results as using the pattern returned by
* FcNameParse; using the latter results in the desired fallback
* behaviour whereas the former just results in 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);
return NULL;
* behaviour whereas the former just results in
* missing-character-rectangles being drawn, at least with some fonts.
*/
if (!(font->xfont = XftFontOpenName(drw->dpy, drw->screen, fontname)) ||
!(font->pattern = FcNameParse((const FcChar8 *) fontname))) {
if (font->xfont) {
XftFontClose(drw->dpy, font->xfont);
font->xfont = NULL;
}
if (!(pattern = FcNameParse((FcChar8 *) fontname))) {
fprintf(stderr, "error, cannot parse font name to pattern: '%s'\n", fontname);
XftFontClose(drw->dpy, xfont);
return NULL;
fprintf(stderr, "error, cannot load font: '%s'\n", fontname);
}
} else if (fontpattern) {
if (!(xfont = XftFontOpenPattern(drw->dpy, fontpattern))) {
fprintf(stderr, "error, cannot load font from pattern.\n");
if (!(font->xfont = XftFontOpenPattern(drw->dpy, fontpattern))) {
fprintf(stderr, "error, cannot load font pattern.\n");
} else {
font->pattern = NULL;
}
}
if (!font->xfont) {
free(font);
return NULL;
}
} else {
die("no font specified.");
}
font = ecalloc(1, sizeof(Fnt));
font->xfont = xfont;
font->pattern = pattern;
font->h = xfont->ascent + xfont->descent;
font->ascent = font->xfont->ascent;
font->descent = font->xfont->descent;
font->h = font->ascent + font->descent;
font->dpy = drw->dpy;
return font;
}
static void
xfont_free(Fnt *font)
{
if (!font)
void
drw_xfont_free(Fnt *font) {
if(!font)
return;
if (font->pattern)
if(font->pattern)
FcPatternDestroy(font->pattern);
XftFontClose(font->dpy, font->xfont);
free(font);
}
Fnt*
drw_fontset_create(Drw* drw, const char *fonts[], size_t fontcount)
{
Fnt *cur, *ret = NULL;
size_t i;
if (!drw || !fonts)
return NULL;
for (i = 1; i <= fontcount; i++) {
if ((cur = xfont_create(drw, fonts[fontcount - i], NULL))) {
drw_fontset_create(Drw* drw, const char *fonts[], size_t fontcount) {
Fnt *ret = NULL;
Fnt *cur = NULL;
ssize_t i;
for (i = fontcount - 1; i >= 0; i--) {
if ((cur = drw_font_xcreate(drw, fonts[i], NULL))) {
cur->next = ret;
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;
}
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)
drw->fonts = set;
}
void
drw_setscheme(Drw *drw, Clr *scm)
{
if (drw)
drw_setscheme(Drw *drw, Scm *scm) {
if (drw && 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)
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)
XFillRectangle(drw->dpy, drw->drawable, drw->gc, x, y, w, h);
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
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];
int ty;
int tx, ty, th;
unsigned int ew;
XftDraw *d = NULL;
Colormap cmap;
Visual *vis;
XftDraw *d;
Fnt *usedfont, *curfont, *nextfont;
size_t i, len;
int utf8strlen, utf8charlen, render = x || y || w || h;
int utf8strlen, utf8charlen, render;
long utf8codepoint = 0;
const char *utf8str;
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;
int charexists = 0;
if (!drw || (render && !drw->scheme) || !text || !drw->fonts)
return 0;
if (!render) {
if (!(render = x || y || w || h)) {
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);
d = XftDrawCreate(drw->dpy, drw->drawable,
DefaultVisual(drw->dpy, drw->screen),
DefaultColormap(drw->dpy, drw->screen));
x += lpad;
w -= lpad;
}
if (!text || !drw->fonts) {
return 0;
} 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;
@ -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;
else
} else {
charexists = 0;
}
}
if (utf8strlen) {
drw_font_getexts(usedfont, utf8str, utf8strlen, &ew, NULL);
/* 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);
if (len) {
memcpy(buf, utf8str, len);
buf[len] = '\0';
if (len < utf8strlen)
for (i = len; i && i > len - 3; buf[--i] = '.')
; /* NOP */
if(len < utf8strlen)
for(i = len; i && i > len - 3; buf[--i] = '.');
if (render) {
ty = y + (h - usedfont->h) / 2 + usedfont->xfont->ascent;
XftDrawStringUtf8(d, &drw->scheme[invert ? ColBg : ColFg],
usedfont->xfont, x, ty, (XftChar8 *)buf, len);
th = usedfont->ascent + usedfont->descent;
ty = y + (h / 2) - (th / 2) + usedfont->ascent;
tx = x + (h / 2);
XftDrawStringUtf8(d, invert ? &drw->scheme->bg.rgb : &drw->scheme->fg.rgb, usedfont->xfont, tx, ty, (XftChar8 *)buf, len);
}
x += 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;
} else {
/* Regardless of whether or not a fallback font is found, the
* character must be drawn. */
* character must be drawn.
*/
charexists = 1;
fccharset = FcCharSetCreate();
FcCharSetAddChar(fccharset, utf8codepoint);
if (!drw->fonts->pattern) {
/* Refer to the comment in xfont_create for more information. */
die("the first font in the cache must be loaded from a font string.");
/* Refer to the comment in drw_font_xcreate for more
* information.
*/
die("The first font in the cache must be loaded from a font string.\n");
}
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);
if (match) {
usedfont = xfont_create(drw, NULL, match);
usedfont = drw_font_xcreate(drw, NULL, match);
if (usedfont && XftCharExists(drw->dpy, usedfont->xfont, utf8codepoint)) {
for (curfont = drw->fonts; curfont->next; curfont = curfont->next)
; /* NOP */
; /* just find the end of the linked list */
curfont->next = usedfont;
} else {
xfont_free(usedfont);
drw_xfont_free(usedfont);
usedfont = drw->fonts;
}
}
}
}
if (d)
XftDrawDestroy(d);
return x + (render ? w : 0);
if (render) {
XftDrawDestroy(d);
}
return x;
}
void
drw_map(Drw *drw, Window win, int x, int y, unsigned int w, unsigned int h)
{
if (!drw)
drw_map(Drw *drw, Window win, int x, int y, unsigned int w, unsigned int h) {
if(!drw)
return;
XCopyArea(drw->dpy, drw->drawable, win, drw->gc, x, y, w, h, x, y);
XSync(drw->dpy, False);
}
unsigned int
drw_fontset_getwidth(Drw *drw, const char *text)
{
drw_fontset_getwidth(Drw *drw, const char *text) {
if (!drw || !drw->fonts || !text)
return 0;
return drw_text(drw, 0, 0, 0, 0, 0, text, 0);
return drw_text(drw, 0, 0, 0, 0, text, 0);
}
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;
if (!font || !text)
if(!font || !text)
return;
XftTextExtentsUtf8(font->dpy, font->xfont, (XftChar8 *)text, len, &ext);
if (w)
*w = ext.xOff;
@ -398,24 +395,19 @@ drw_font_getexts(Fnt *font, const char *text, unsigned int len, unsigned int *w,
}
Cur *
drw_cur_create(Drw *drw, int shape)
{
Cur *cur;
drw_cur_create(Drw *drw, int shape) {
Cur *cur = (Cur *)calloc(1, sizeof(Cur));
if (!drw || !(cur = ecalloc(1, sizeof(Cur))))
if(!drw || !cur)
return NULL;
cur->cursor = XCreateFontCursor(drw->dpy, shape);
return cur;
}
void
drw_cur_free(Drw *drw, Cur *cursor)
{
if (!cursor)
drw_cur_free(Drw *drw, Cur *cursor) {
if(!drw || !cursor)
return;
XFreeCursor(drw->dpy, cursor->cursor);
free(cursor);
}

28
drw.h
View File

@ -1,19 +1,27 @@
/* See LICENSE file for copyright and license details. */
#define DRW_FONT_CACHE_SIZE 32
typedef struct {
Cursor cursor;
} Cur;
typedef struct Fnt {
typedef struct Fnt Fnt;
struct Fnt {
Display *dpy;
int ascent;
int descent;
unsigned int h;
XftFont *xfont;
FcPattern *pattern;
struct Fnt *next;
} Fnt;
Fnt *next;
};
enum { ColFg, ColBg }; /* Clr scheme index */
typedef XftColor Clr;
typedef struct {
struct {
unsigned long pix;
XftColor rgb;
} fg, bg;
} Scm;
typedef struct {
unsigned int w, h;
@ -22,7 +30,7 @@ typedef struct {
Window root;
Drawable drawable;
GC gc;
Clr *scheme;
Scm *scheme;
Fnt *fonts;
} 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);
/* Colorscheme abstraction */
void drw_clr_create(Drw *drw, Clr *dest, const char *clrname);
Clr *drw_scm_create(Drw *drw, const char *clrnames[], size_t clrcount);
Scm *drw_scm_create(Drw *drw, const char *fgname, const char *bgname);
void drw_scm_free(Scm *scm);
/* Cursor abstraction */
Cur *drw_cur_create(Drw *drw, int shape);
@ -47,11 +55,11 @@ void drw_cur_free(Drw *drw, Cur *cursor);
/* Drawing context manipulation */
void drw_setfontset(Drw *drw, Fnt *set);
void drw_setscheme(Drw *drw, Clr *scm);
void drw_setscheme(Drw *drw, Scm *scm);
/* Drawing functions */
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 */
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
♽ Xlib
☢ Xft
☃ farbfeld
☢ libpng
~1000 lines of code
@ -30,8 +29,8 @@ $ sent FILE1 [FILE2 …]
▸ one slide per paragraph
▸ lines starting with # are ignored
image slide: paragraph containing @FILENAME
▸ empty slide: just use a \ as a paragraph
paragraphs starting with a @ line are png images
for an empty slide just use a \ as a paragraph
# This is a comment and will not be part of the presentation
@ -50,20 +49,10 @@ $ sent FILE1 [FILE2 …]
\#This line as well
⇒ 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?

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

519
sent.c
View File

@ -1,17 +1,12 @@
/* See LICENSE file for copyright and license details. */
#include <sys/types.h>
#include <arpa/inet.h>
/* See LICENSE for licence details. */
#include <errno.h>
#include <fcntl.h>
#include <math.h>
#include <regex.h>
#include <png.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <X11/keysym.h>
#include <X11/XKBlib.h>
#include <X11/Xatom.h>
@ -20,7 +15,6 @@
#include <X11/Xft/Xft.h>
#include "arg.h"
#include "util.h"
#include "drw.h"
char *argv0;
@ -32,7 +26,9 @@ char *argv0;
typedef enum {
NONE = 0,
SCALED = 1,
LOADED = 1,
SCALED = 2,
DRAWN = 4
} imgstate;
typedef struct {
@ -40,19 +36,16 @@ typedef struct {
unsigned int bufwidth, bufheight;
imgstate state;
XImage *ximg;
FILE *f;
png_structp png_ptr;
png_infop info_ptr;
int numpasses;
} Image;
typedef struct {
char *regex;
char *bin;
} Filter;
typedef struct {
unsigned int linecount;
char **lines;
Image *img;
char *embed;
} Slide;
/* Purely graphic info */
@ -86,25 +79,27 @@ typedef struct {
const Arg arg;
} Shortcut;
static void fffree(Image *img);
static void ffload(Slide *s);
static void ffprepare(Image *img);
static void ffscale(Image *img);
static void ffdraw(Image *img);
static Image *pngopen(char *filename);
static void pngfree(Image *img);
static int pngread(Image *img);
static int pngprepare(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 cleanup(int slidesonly);
static void reload(const Arg *arg);
static void cleanup();
static void eprintf(const char *, ...);
static void die(const char *, ...);
static void load(FILE *fp);
static void advance(const Arg *arg);
static void quit(const Arg *arg);
static void resize(int width, int height);
static void run(void);
static void usage(void);
static void xdraw(void);
static void xhints(void);
static void xinit(void);
static void xloadfonts(void);
static void run();
static void usage();
static void xdraw();
static void xhints();
static void xinit();
static void xloadfonts();
static void bpress(XEvent *);
static void cmessage(XEvent *);
@ -116,13 +111,12 @@ static void configure(XEvent *);
#include "config.h"
/* Globals */
static const char *fname = NULL;
static Slide *slides = NULL;
static int idx = 0;
static int slidecount = 0;
static XWindow xw;
static Drw *d = NULL;
static Clr *sc;
static Scm *sc;
static Fnt *fonts[NUMFONTSCALES];
static int running = 1;
@ -134,128 +128,118 @@ static void (*handler[LASTEvent])(XEvent *) = {
[KeyPress] = kpress,
};
int
filter(int fd, const char *cmd)
Image *pngopen(char *filename)
{
int fds[2];
FILE *f;
unsigned char buf[8];
Image *img;
if (pipe(fds) < 0)
die("sent: Unable to create pipe:");
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);
if (!(f = fopen(filename, "rb"))) {
eprintf("could not open file %s:", filename);
return NULL;
}
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
fffree(Image *img)
void pngfree(Image *img)
{
png_destroy_read_struct(&img->png_ptr, img->info_ptr ? &img->info_ptr : NULL, NULL);
free(img->buf);
if (img->ximg)
XDestroyImage(img->ximg);
free(img);
}
void
ffload(Slide *s)
int pngread(Image *img)
{
uint32_t y, x;
uint16_t *row;
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;
unsigned int y;
png_bytepp row_pointers;
if (s->img || !(filename = s->embed) || !s->embed[0])
return; /* already done */
if (!img)
return 0;
for (i = 0; i < LEN(filters); i++) {
if (regcomp(&regex, filters[i].regex,
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 (img->state & LOADED)
return 2;
if ((fdin = open(filename, O_RDONLY)) < 0)
die("sent: Unable to open '%s':", filename);
if (img->buf)
free(img->buf);
if (!(img->buf = malloc(3 * img->bufwidth * img->bufheight)))
return 0;
if ((fdout = filter(fdin, bin)) < 0)
die("sent: Unable to filter '%s':", filename);
close(fdin);
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;
}
if (setjmp(png_jmpbuf(img->png_ptr))) {
png_destroy_read_struct(&img->png_ptr, &img->info_ptr, NULL);
return 0;
}
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
ffprepare(Image *img)
int pngprepare(Image *img)
{
int depth = DefaultDepth(xw.dpy, xw.scr);
int width = xw.uw;
@ -266,26 +250,38 @@ ffprepare(Image *img)
else
height = img->bufheight * xw.uw / img->bufwidth;
if (depth < 24)
die("sent: Display color depths < 24 not supported");
if (img->ximg)
XDestroyImage(img->ximg);
if (depth < 24) {
eprintf("display depths <24 not supported.");
return 0;
}
if (!(img->ximg = XCreateImage(xw.dpy, CopyFromParent, depth, ZPixmap, 0,
NULL, width, height, 32, 0)))
die("sent: Unable to create XImage");
NULL, width, height, 32, 0))) {
eprintf("could not create XImage");
return 0;
}
img->ximg->data = ecalloc(height, img->ximg->bytes_per_line);
if (!XInitImage(img->ximg))
die("sent: Unable to initiate XImage");
if (!(img->ximg->data = malloc(img->ximg->bytes_per_line * height))) {
eprintf("could not alloc data section for 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;
return 1;
}
void
ffscale(Image *img)
void pngscale(Image *img)
{
unsigned int x, y;
unsigned int width = img->ximg->width;
@ -310,103 +306,104 @@ ffscale(Image *img)
}
}
void
ffdraw(Image *img)
void pngdraw(Image *img)
{
int xoffset = (xw.w - img->ximg->width) / 2;
int yoffset = (xw.h - img->ximg->height) / 2;
XPutImage(xw.dpy, xw.win, d->gc, img->ximg, 0, 0,
xoffset, yoffset, img->ximg->width, img->ximg->height);
XFlush(xw.dpy);
img->state |= DRAWN;
}
void
getfontsize(Slide *s, unsigned int *width, unsigned int *height)
void getfontsize(Slide *s, unsigned int *width, unsigned int *height)
{
int i, j;
unsigned int curw, newmax;
size_t i, j;
unsigned int curw, imax;
float lfac = linespacing * (s->linecount - 1) + 1;
/* fit height */
for (j = NUMFONTSCALES - 1; j >= 0; j--)
if (fonts[j]->h * lfac <= xw.uh)
break;
LIMIT(j, 0, NUMFONTSCALES - 1);
drw_setfontset(d, fonts[j]);
/* fit width */
*width = 0;
for (i = 0; i < s->linecount; i++) {
curw = drw_fontset_getwidth(d, s->lines[i]);
newmax = (curw >= *width);
while (j > 0 && curw > xw.uw) {
if (curw >= *width)
imax = i;
while (j >= 0 && curw > xw.uw) {
drw_setfontset(d, fonts[--j]);
curw = drw_fontset_getwidth(d, s->lines[i]);
}
if (newmax)
if (imax == i)
*width = curw;
}
*height = fonts[j]->h * lfac;
*width += fonts[j]->h;
}
void
cleanup(int slidesonly)
void cleanup()
{
unsigned int i, j;
if (!slidesonly) {
for (i = 0; i < NUMFONTSCALES; i++)
drw_fontset_free(fonts[i]);
free(sc);
drw_scm_free(sc);
drw_free(d);
XDestroyWindow(xw.dpy, xw.win);
XSync(xw.dpy, False);
XCloseDisplay(xw.dpy);
}
if (slides) {
for (i = 0; i < slidecount; i++) {
for (j = 0; j < slides[i].linecount; j++)
free(slides[i].lines[j]);
free(slides[i].lines);
if (slides[i].img)
fffree(slides[i].img);
pngfree(slides[i].img);
}
if (!slidesonly) {
free(slides);
slides = NULL;
}
}
}
void
reload(const Arg *arg)
void die(const char *fmt, ...)
{
FILE *fp = NULL;
unsigned int i;
va_list ap;
if (!fname) {
fprintf(stderr, "sent: Cannot reload from stdin. Use a file!\n");
return;
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);
}
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();
exit(1);
}
void
load(FILE *fp)
void eprintf(const char *fmt, ...)
{
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;
size_t blen, maxlines;
@ -415,6 +412,10 @@ load(FILE *fp)
/* read each line from fp and add it to the item list */
while (1) {
if ((slidecount+1) * sizeof(*slides) >= size)
if (!(slides = realloc(slides, (size += BUFSIZ))))
die("cannot realloc %u bytes:", size);
/* eat consecutive empty lines */
while ((p = fgets(buf, sizeof(buf), fp)))
if (strcmp(buf, "\n") != 0 && buf[0] != '#')
@ -422,18 +423,10 @@ load(FILE *fp)
if (!p)
break;
if ((slidecount+1) * sizeof(*slides) >= size)
if (!(slides = realloc(slides, (size += BUFSIZ))))
die("sent: Unable to reallocate %u bytes:", size);
/* read one slide */
maxlines = 0;
memset((s = &slides[slidecount]), 0, sizeof(Slide));
do {
/* if there's a leading null, we can't do blen-1 */
if (buf[0] == '\0')
continue;
if (buf[0] == '#')
continue;
@ -441,54 +434,53 @@ load(FILE *fp)
if (s->linecount >= maxlines) {
maxlines = 2 * s->linecount + 1;
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);
if (!(s->lines[s->linecount] = strdup(buf)))
die("sent: Unable to strdup:");
die("cannot strdup:");
if (s->lines[s->linecount][blen-1] == '\n')
s->lines[s->linecount][blen-1] = '\0';
/* mark as image slide if first line of a slide starts with @ */
if (s->linecount == 0 && s->lines[0][0] == '@')
s->embed = &s->lines[0][1];
/* only make image slide if first line of a slide starts with @ */
if (s->linecount == 0 && s->lines[0][0] == '@') {
memmove(s->lines[0], &s->lines[0][1], blen);
s->img = pngopen(s->lines[0]);
}
if (s->lines[s->linecount][0] == '\\')
memmove(s->lines[s->linecount], &s->lines[s->linecount][1], blen);
s->linecount++;
} while ((p = fgets(buf, sizeof(buf), fp)) && strcmp(buf, "\n") != 0);
slidecount++;
if (!p)
break;
}
if (!slidecount)
die("sent: No slides in file");
}
void
advance(const Arg *arg)
void advance(const Arg *arg)
{
int new_idx = idx + arg->i;
LIMIT(new_idx, 0, slidecount-1);
if (new_idx != idx) {
if (slides[idx].img)
slides[idx].img->state &= ~SCALED;
slides[idx].img->state &= ~(DRAWN | SCALED);
idx = new_idx;
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
quit(const Arg *arg)
void quit(const Arg *arg)
{
running = 0;
}
void
resize(int width, int height)
void resize(int width, int height)
{
xw.w = width;
xw.h = height;
@ -497,8 +489,7 @@ resize(int width, int height)
drw_resize(d, width, height);
}
void
run(void)
void run()
{
XEvent ev;
@ -519,8 +510,13 @@ run(void)
}
}
void
xdraw(void)
void usage()
{
die("sent " VERSION " (c) 2014-2015 markus.teich@stusta.mhn.de\n" \
"usage: sent FILE1 [FILE2 ...]", argv0);
}
void xdraw()
{
unsigned int height, width, i;
Image *im = slides[idx].img;
@ -536,26 +532,26 @@ xdraw(void)
(xw.h - height) / 2 + i * linespacing * d->fonts->h,
width,
d->fonts->h,
0,
slides[idx].lines[i],
0);
drw_map(d, xw.win, 0, 0, xw.w, xw.h);
} else {
if (!(im->state & SCALED))
ffprepare(im);
ffdraw(im);
} else if (!(im->state & LOADED) && !pngread(im)) {
eprintf("could not read image %s", slides[idx].lines[0]);
} else if (!(im->state & SCALED) && !pngprepare(im)) {
eprintf("could not prepare image %s for drawing", slides[idx].lines[0]);
} else if (!(im->state & DRAWN)) {
pngdraw(im);
}
}
void
xhints(void)
void xhints()
{
XClassHint class = {.res_name = "sent", .res_class = "presenter"};
XWMHints wm = {.flags = InputHint, .input = True};
XSizeHints *sizeh = NULL;
if (!(sizeh = XAllocSizeHints()))
die("sent: Unable to allocate size hints");
die("sent: Could not alloc size hints");
sizeh->flags = PSize;
sizeh->height = xw.h;
@ -565,40 +561,35 @@ xhints(void)
XFree(sizeh);
}
void
xinit(void)
void xinit()
{
XTextProperty prop;
unsigned int i;
if (!(xw.dpy = XOpenDisplay(NULL)))
die("sent: Unable to open display");
die("Can't open display.");
xw.scr = XDefaultScreen(xw.dpy);
xw.vis = XDefaultVisual(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.event_mask = KeyPressMask | ExposureMask | StructureNotifyMask |
ButtonMotionMask | ButtonPressMask;
xw.attrs.event_mask = KeyPressMask | ExposureMask | StructureNotifyMask
| ButtonMotionMask | ButtonPressMask;
xw.win = XCreateWindow(xw.dpy, XRootWindow(xw.dpy, xw.scr), 0, 0,
xw.w, xw.h, 0, XDefaultDepth(xw.dpy, xw.scr),
InputOutput, xw.vis, CWBitGravity | CWEventMask,
&xw.attrs);
xw.w, xw.h, 0, XDefaultDepth(xw.dpy, xw.scr), InputOutput, xw.vis,
CWBackPixel | CWBitGravity | CWEventMask, &xw.attrs);
xw.wmdeletewin = XInternAtom(xw.dpy, "WM_DELETE_WINDOW", False);
xw.netwmname = XInternAtom(xw.dpy, "_NET_WM_NAME", False);
XSetWMProtocols(xw.dpy, xw.win, &xw.wmdeletewin, 1);
if (!(d = drw_create(xw.dpy, xw.scr, xw.win, xw.w, xw.h)))
die("sent: Unable to create drawing context");
sc = drw_scm_create(d, colors, 2);
die("Can't create drawing context.");
sc = drw_scm_create(d, fgcol, bgcol);
drw_setscheme(d, sc);
XSetWindowBackground(xw.dpy, xw.win, sc[ColBg].pixel);
xloadfonts();
for (i = 0; i < slidecount; i++)
ffload(&slides[i]);
XStringListToTextProperty(&argv0, 1, &prop);
XSetWMName(xw.dpy, xw.win, &prop);
@ -609,31 +600,30 @@ xinit(void)
XSync(xw.dpy, False);
}
void
xloadfonts(void)
void xloadfonts()
{
int i, j;
char *fstrs[LEN(fontfallbacks)];
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 (j = 0; j < LEN(fontfallbacks); j++) {
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))))
die("sent: Unable to load any font for size %d", FONTSZ(i));
fonts[i] = drw_fontset_create(d, (const char**)fstrs, LEN(fstrs));
}
for (j = 0; j < LEN(fontfallbacks); j++)
if (fstrs[j])
free(fstrs[j]);
}
void
bpress(XEvent *e)
void bpress(XEvent *e)
{
unsigned int i;
@ -642,22 +632,19 @@ bpress(XEvent *e)
mshortcuts[i].func(&(mshortcuts[i].arg));
}
void
cmessage(XEvent *e)
void cmessage(XEvent *e)
{
if (e->xclient.data.l[0] == xw.wmdeletewin)
running = 0;
}
void
expose(XEvent *e)
void expose(XEvent *e)
{
if (0 == e->xexpose.count)
xdraw();
}
void
kpress(XEvent *e)
void kpress(XEvent *e)
{
unsigned int i;
KeySym sym;
@ -668,44 +655,40 @@ kpress(XEvent *e)
shortcuts[i].func(&(shortcuts[i].arg));
}
void
configure(XEvent *e)
void configure(XEvent *e)
{
resize(e->xconfigure.width, e->xconfigure.height);
if (slides[idx].img)
slides[idx].img->state &= ~SCALED;
slides[idx].img->state &= ~(DRAWN | SCALED);
xdraw();
}
void
usage(void)
{
die("usage: %s [file]", argv0);
}
int
main(int argc, char *argv[])
int main(int argc, char *argv[])
{
int i;
FILE *fp = NULL;
ARGBEGIN {
case 'v':
fprintf(stderr, "sent-"VERSION"\n");
return 0;
default:
usage();
} ARGEND
} ARGEND;
if (!argv[0] || !strcmp(argv[0], "-"))
fp = stdin;
else if (!(fp = fopen(fname = argv[0], "r")))
die("sent: Unable to open '%s' for reading:", fname);
for (i = 0; i < argc; i++) {
if ((fp = strcmp(argv[i], "-") ? fopen(argv[i], "r") : stdin)) {
load(fp);
fclose(fp);
} else {
eprintf("could not open %s for reading:", argv[i]);
}
}
if (!slides || !slides[0].lines)
usage();
xinit();
run();
cleanup(0);
cleanup();
return 0;
}

Binary file not shown.

26
util.c
View File

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

3
util.h
View File

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