Sent added, scripts and environment

This commit is contained in:
inigoortega 2019-09-14 17:03:35 +02:00
parent 2a72b7bc0b
commit 621cc22b6e
30 changed files with 2572 additions and 8 deletions

View File

@ -45,3 +45,6 @@ Print
shift + Shift_R
mksh $SCRIPTS/toggle-layout.sh && mksh $SCRIPTS/reset-dwmbar.sh
alt + e
mksh $SCRIPTS/vifm.sh

View File

@ -1,6 +1,6 @@
[General]
filedialog-path=@Variant(\0\0\0\x11\0\0\0\r/home/initega)
geometry=@ByteArray(\x1\xd9\xd0\xcb\0\x3\0\0\0\0\0\0\0\0\0\x13\0\0\x4\xff\0\0\x3\x1f\0\0\0\x1\0\0\0\x14\0\0\x4\xfe\0\0\x3\x1e\0\0\0\0\0\0\0\0\x5\0\0\0\0\x1\0\0\0\x14\0\0\x4\xfe\0\0\x3\x1e)
geometry=@ByteArray(\x1\xd9\xd0\xcb\0\x3\0\0\0\0\0\0\0\0\0\x13\0\0\x4\xff\0\0\x3\x1f\0\0\0\x2\0\0\0\x15\0\0\x4\xfd\0\0\x3\x1d\0\0\0\0\0\0\0\0\x5\0\0\0\0\x2\0\0\0\x15\0\0\x4\xfd\0\0\x3\x1d)
[FullScreen]
pos=@Point(0 0)

1
.gitignore vendored
View File

@ -5,3 +5,4 @@
.config/mutt/
.local/share/
termscripts/test.sh
.config/tox

View File

@ -17,8 +17,9 @@ export PASSWORD_STORE_DIR="$HOME/.password-store"
export ANDROID_MOUNTPOINT="$HOME/Android"
export BRIGHTNESS="0.9"
export SUDO_ASKPASS="/usr/bin/x11-ssh-askpass"
export TRASH="$HOME/.local/trash"
export TRASH="$HOME/.local/trash:$HOME/.local/share/vifm/Trash"
export DEVICES_FOLDER="/media"
export LC_ALL=en_US.utf8
# custom prompt see http://comments.gmane.org/gmane.os.miros.mksh/126
# USER

View File

@ -18,6 +18,6 @@ export PASSWORD_STORE_DIR="$HOME/.password-store"
export ANDROID_MOUNTPOINT="$HOME/Android"
export BRIGHTNESS="0.9"
export SUDO_ASKPASS="/usr/bin/x11-ssh-askpass"
export TRASH="$HOME/.local/trash"
export TRASH="$HOME/.local/trash:$HOME/.local/share/vifm/Trash"
export DEVICES_FOLDER="/media"
export LC_ALL=en_US.utf8

3
.zshrc
View File

@ -99,8 +99,9 @@ export PASSWORD_STORE_DIR="$HOME/.password-store"
export ANDROID_MOUNTPOINT="$HOME/Android"
export BRIGHTNESS="0.9"
export SUDO_ASKPASS="/usr/bin/x11-ssh-askpass"
export TRASH="$HOME/.local/trash"
export TRASH="$HOME/.local/trash:$HOME/.local/share/vifm/Trash"
export DEVICES_FOLDER="/media"
export LC_ALL=en_US.utf8
#
# ALIASES

View File

@ -35,8 +35,11 @@ static const Rule rules[] = {
/* class instance title tags mask isfloating monitor */
{ "Gimp", NULL, NULL, 0, 1, -1 },
{ "Firefox", NULL, NULL, 1 << 8, 0, -1 },
{ "TelegramDesktop", NULL, NULL, 1 << 6, 0, -1 },
{ "Workrave", NULL, NULL, ~0, 1, -1 },
{ "Tor Browser", NULL, NULL, 1 << 8, 0, -1 },
{ "TelegramDesktop", NULL, NULL, 1 << 6, 0, -1 },
{ "Workrave", NULL, NULL, ~0, 1, -1 },
{ "mpv", "gl", NULL, 1 << 5, 0, -1 },
{ "vlc", "vlc", NULL, 1 << 5, 0, -1 },
};
/* layout(s) */

Binary file not shown.

Binary file not shown.

View File

@ -7,10 +7,13 @@ formats="$(echo "$formats" | tr ',' '|')"
regex=".*\\.($formats)"
trash="{$(echo "$TRASH" | tr ':' ',')}"
echo $trash
multimedia="$(find "$HOME" -regextype posix-extended -regex "$regex" \
-not -regex "$TRASH.*" | sed "s|^$HOME/||")"
-not -regex "$trash.*" | sed "s|^$HOME/||")"
multimedia="${multimedia}$(find "$DEVICES_FOLDER" -regextype posix-extended \
-regex "$regex" -not -regex "$TRASH.*")"
-regex "$regex" -not -regex "$trash.*")"
media="$(echo "${multimedia}" | dmenu -i -l 9)"

28
sent/LICENSE Normal file
View File

@ -0,0 +1,28 @@
ISC-License
(c) 2014-2016 Markus Teich <markus.teich@stusta.mhn.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.
(c) 2015 Jonas Jelten <jj@sft.mx>
(c) 2015 Szabolcs Nagy <nsz@port70.net>
(c) 2015 Tony Lainson <t.lainson@gmail.com>
(c) 2015 Jan Christoph Ebersbach <jceb@e-jc.de>
(c) 2015 Ivan Tham <pickfire@riseup.net>
(c) 2015 Quentin Rameau <quinq@fifth.space>
(c) 2015 Alexis <surryhill@gmail.com>
(c) 2015 Dimitris Papastamos <sin@2f30.org>
(c) 2015 Grant Mathews <grant.m.mathews@gmail.com>
(c) 2015 David Phillips <dbphillipsnz@gmail.com>
(c) 2016 Laslo Hunhold <dev@frign.de>
(c) 2016 Hiltjo Posthuma <hiltjo@codemadness.org>

60
sent/Makefile Normal file
View File

@ -0,0 +1,60 @@
# sent - plain text presentation tool
# See LICENSE file for copyright and license details.
include config.mk
SRC = sent.c drw.c util.c
OBJ = ${SRC:.c=.o}
all: options sent
options:
@echo sent build options:
@echo "CFLAGS = ${CFLAGS}"
@echo "LDFLAGS = ${LDFLAGS}"
@echo "CC = ${CC}"
config.h:
cp config.def.h config.h
.c.o:
@echo CC $<
@${CC} -c ${CFLAGS} $<
${OBJ}: config.h config.mk
sent: ${OBJ}
@echo CC -o $@
@${CC} -o $@ ${OBJ} ${LDFLAGS}
cscope: ${SRC} config.h
@echo cScope
@cscope -R -b || echo cScope not installed
clean:
@echo cleaning
@rm -f sent ${OBJ} sent-${VERSION}.tar.gz
dist: clean
@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
@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
@echo installing manual page to ${DESTDIR}${MANPREFIX}/man1
@mkdir -p ${DESTDIR}${MANPREFIX}/man1
@cp sent.1 ${DESTDIR}${MANPREFIX}/man1/sent.1
@chmod 644 ${DESTDIR}${MANPREFIX}/man1/sent.1
uninstall:
@echo removing executable file from ${DESTDIR}${PREFIX}/bin
@rm -f ${DESTDIR}${PREFIX}/bin/sent
.PHONY: all options clean dist install uninstall cscope

59
sent/README.md Normal file
View File

@ -0,0 +1,59 @@
sent is 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.
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.
Demo
To get a little demo, just type
make && ./sent example
You can navigate with the arrow keys and quit with `q`.
Usage
sent [FILE]
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:
sent
@nyan.png
depends on
- Xlib
- Xft
- farbfeld
sent FILENAME
one slide per paragraph
# This is a comment and will not be part of the presentation
\# This and the next line start with backslashes
\@FILE.png
thanks / questions?
Development
sent is developed at http://tools.suckless.org/sent
0: http://tools.suckless.org/farbfeld/

49
sent/arg.h Normal file
View File

@ -0,0 +1,49 @@
/*
* 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.
*/
#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; \
} \
} \
} \
}
#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)
#endif

59
sent/config.def.h Normal file
View File

@ -0,0 +1,59 @@
/* See LICENSE file for copyright and license details. */
static char *fontfallbacks[] = {
"dejavu sans",
"roboto",
"ubuntu",
};
#define NUMFONTSCALES 42
#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 float linespacing = 1.4;
/* how much screen estate is to be used at max for the content */
static const float usablewidth = 0.75;
static const float usableheight = 0.75;
/* height of the presentation progress bar */
static const int progressheight = 5;
static Mousekey mshortcuts[] = {
/* button function argument */
{ Button1, advance, {.i = +1} },
{ Button3, advance, {.i = -1} },
{ Button4, advance, {.i = -1} },
{ Button5, 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} },
{ XK_j, advance, {.i = +1} },
{ XK_k, advance, {.i = -1} },
{ XK_Down, advance, {.i = +1} },
{ 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" },
};

59
sent/config.h Normal file
View File

@ -0,0 +1,59 @@
/* See LICENSE file for copyright and license details. */
static char *fontfallbacks[] = {
"dejavu sans",
"roboto",
"ubuntu",
};
#define NUMFONTSCALES 42
#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 float linespacing = 1.4;
/* how much screen estate is to be used at max for the content */
static const float usablewidth = 0.75;
static const float usableheight = 0.75;
/* height of the presentation progress bar */
static const int progressheight = 5;
static Mousekey mshortcuts[] = {
/* button function argument */
{ Button1, advance, {.i = +1} },
{ Button3, advance, {.i = -1} },
{ Button4, advance, {.i = -1} },
{ Button5, 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} },
{ XK_j, advance, {.i = +1} },
{ XK_k, advance, {.i = -1} },
{ XK_Down, advance, {.i = +1} },
{ 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" },
};

30
sent/config.mk Normal file
View File

@ -0,0 +1,30 @@
# sent version
VERSION = 1
# Customize below to fit your system
# paths
PREFIX = /usr/local
MANPREFIX = ${PREFIX}/share/man
X11INC = /usr/X11R6/include
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
# flags
CPPFLAGS = -DVERSION=\"${VERSION}\" -D_XOPEN_SOURCE=600
CFLAGS += -g -std=c99 -pedantic -Wall ${INCS} ${CPPFLAGS}
LDFLAGS += -g ${LIBS}
#CFLAGS += -std=c99 -pedantic -Wall -Os ${INCS} ${CPPFLAGS}
#LDFLAGS += ${LIBS}
# compiler and linker
CC ?= cc

421
sent/drw.c Normal file
View File

@ -0,0 +1,421 @@
/* See LICENSE file for copyright and license details. */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <X11/Xlib.h>
#include <X11/Xft/Xft.h>
#include "drw.h"
#include "util.h"
#define UTF_INVALID 0xFFFD
#define UTF_SIZ 4
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])
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))
*u = UTF_INVALID;
for (i = 1; *u > utfmax[i]; ++i)
;
return i;
}
static size_t
utf8decode(const char *c, long *u, size_t clen)
{
size_t i, j, len, type;
long udecoded;
*u = UTF_INVALID;
if (!clen)
return 0;
udecoded = utf8decodebyte(c[0], &len);
if (!BETWEEN(len, 1, UTF_SIZ))
return 1;
for (i = 1, j = 1; i < clen && j < len; ++i, ++j) {
udecoded = (udecoded << 6) | utf8decodebyte(c[i], &type);
if (type)
return j;
}
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->dpy = dpy;
drw->screen = screen;
drw->root = root;
drw->w = w;
drw->h = 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)
return;
drw->w = w;
drw->h = h;
if (drw->drawable)
XFreePixmap(drw->dpy, drw->drawable);
drw->drawable = XCreatePixmap(drw->dpy, drw->root, w, h, DefaultDepth(drw->dpy, drw->screen));
}
void
drw_free(Drw *drw)
{
XFreePixmap(drw->dpy, drw->drawable);
XFreeGC(drw->dpy, drw->gc);
free(drw);
}
/* This function is an implementation detail. Library users should use
* drw_fontset_create instead.
*/
static Fnt *
xfont_create(Drw *drw, const char *fontname, FcPattern *fontpattern)
{
Fnt *font;
XftFont *xfont = NULL;
FcPattern *pattern = 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;
}
if (!(pattern = FcNameParse((FcChar8 *) fontname))) {
fprintf(stderr, "error, cannot parse font name to pattern: '%s'\n", fontname);
XftFontClose(drw->dpy, xfont);
return NULL;
}
} else if (fontpattern) {
if (!(xfont = XftFontOpenPattern(drw->dpy, fontpattern))) {
fprintf(stderr, "error, cannot load font from pattern.\n");
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->dpy = drw->dpy;
return font;
}
static void
xfont_free(Fnt *font)
{
if (!font)
return;
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))) {
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)
{
if (drw)
drw->fonts = set;
}
void
drw_setscheme(Drw *drw, Clr *scm)
{
if (drw)
drw->scheme = scm;
}
void
drw_rect(Drw *drw, int x, int y, unsigned int w, unsigned int h, int filled, int invert)
{
if (!drw || !drw->scheme)
return;
XSetForeground(drw->dpy, drw->gc, invert ? drw->scheme[ColBg].pixel : drw->scheme[ColFg].pixel);
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);
}
int
drw_text(Drw *drw, int x, int y, unsigned int w, unsigned int h, unsigned int lpad, const char *text, int invert)
{
char buf[1024];
int ty;
unsigned int ew;
XftDraw *d = NULL;
Fnt *usedfont, *curfont, *nextfont;
size_t i, len;
int utf8strlen, utf8charlen, render = x || y || w || h;
long utf8codepoint = 0;
const char *utf8str;
FcCharSet *fccharset;
FcPattern *fcpattern;
FcPattern *match;
XftResult result;
int charexists = 0;
if (!drw || (render && !drw->scheme) || !text || !drw->fonts)
return 0;
if (!render) {
w = ~w;
} else {
XSetForeground(drw->dpy, drw->gc, drw->scheme[invert ? ColFg : ColBg].pixel);
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;
}
usedfont = drw->fonts;
while (1) {
utf8strlen = 0;
utf8str = text;
nextfont = NULL;
while (*text) {
utf8charlen = utf8decode(text, &utf8codepoint, UTF_SIZ);
for (curfont = drw->fonts; curfont; curfont = curfont->next) {
charexists = charexists || XftCharExists(drw->dpy, curfont->xfont, utf8codepoint);
if (charexists) {
if (curfont == usedfont) {
utf8strlen += utf8charlen;
text += utf8charlen;
} else {
nextfont = curfont;
}
break;
}
}
if (!charexists || nextfont)
break;
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--)
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 (render) {
ty = y + (h - usedfont->h) / 2 + usedfont->xfont->ascent;
XftDrawStringUtf8(d, &drw->scheme[invert ? ColBg : ColFg],
usedfont->xfont, x, ty, (XftChar8 *)buf, len);
}
x += ew;
w -= ew;
}
}
if (!*text) {
break;
} else if (nextfont) {
charexists = 0;
usedfont = nextfont;
} else {
/* Regardless of whether or not a fallback font is found, the
* 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.");
}
fcpattern = FcPatternDuplicate(drw->fonts->pattern);
FcPatternAddCharSet(fcpattern, FC_CHARSET, fccharset);
FcPatternAddBool(fcpattern, FC_SCALABLE, FcTrue);
FcConfigSubstitute(NULL, fcpattern, FcMatchPattern);
FcDefaultSubstitute(fcpattern);
match = XftFontMatch(drw->dpy, drw->screen, fcpattern, &result);
FcCharSetDestroy(fccharset);
FcPatternDestroy(fcpattern);
if (match) {
usedfont = xfont_create(drw, NULL, match);
if (usedfont && XftCharExists(drw->dpy, usedfont->xfont, utf8codepoint)) {
for (curfont = drw->fonts; curfont->next; curfont = curfont->next)
; /* NOP */
curfont->next = usedfont;
} else {
xfont_free(usedfont);
usedfont = drw->fonts;
}
}
}
}
if (d)
XftDrawDestroy(d);
return x + (render ? w : 0);
}
void
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)
{
if (!drw || !drw->fonts || !text)
return 0;
return drw_text(drw, 0, 0, 0, 0, 0, text, 0);
}
void
drw_font_getexts(Fnt *font, const char *text, unsigned int len, unsigned int *w, unsigned int *h)
{
XGlyphInfo ext;
if (!font || !text)
return;
XftTextExtentsUtf8(font->dpy, font->xfont, (XftChar8 *)text, len, &ext);
if (w)
*w = ext.xOff;
if (h)
*h = font->h;
}
Cur *
drw_cur_create(Drw *drw, int shape)
{
Cur *cur;
if (!drw || !(cur = ecalloc(1, sizeof(Cur))))
return NULL;
cur->cursor = XCreateFontCursor(drw->dpy, shape);
return cur;
}
void
drw_cur_free(Drw *drw, Cur *cursor)
{
if (!cursor)
return;
XFreeCursor(drw->dpy, cursor->cursor);
free(cursor);
}

57
sent/drw.h Normal file
View File

@ -0,0 +1,57 @@
/* See LICENSE file for copyright and license details. */
typedef struct {
Cursor cursor;
} Cur;
typedef struct Fnt {
Display *dpy;
unsigned int h;
XftFont *xfont;
FcPattern *pattern;
struct Fnt *next;
} Fnt;
enum { ColFg, ColBg }; /* Clr scheme index */
typedef XftColor Clr;
typedef struct {
unsigned int w, h;
Display *dpy;
int screen;
Window root;
Drawable drawable;
GC gc;
Clr *scheme;
Fnt *fonts;
} Drw;
/* Drawable abstraction */
Drw *drw_create(Display *dpy, int screen, Window win, unsigned int w, unsigned int h);
void drw_resize(Drw *drw, unsigned int w, unsigned int h);
void drw_free(Drw *drw);
/* Fnt abstraction */
Fnt *drw_fontset_create(Drw* drw, const char *fonts[], size_t fontcount);
void drw_fontset_free(Fnt* set);
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);
/* Cursor abstraction */
Cur *drw_cur_create(Drw *drw, int shape);
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);
/* 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);
/* Map functions */
void drw_map(Drw *drw, Window win, int x, int y, unsigned int w, unsigned int h);

69
sent/example Normal file
View File

@ -0,0 +1,69 @@
sent
Origin:
Takahashi
Why?
• PPTX sucks
• LATEX sucks
• PDF sucks
also:
terminal presentations
don't support images…
@nyan.png
this text will not be displayed, since the @ at the start of the first line
makes this paragraph an image slide.
easy to use
depends on
♽ Xlib
☢ Xft
☃ farbfeld
~1000 lines of code
usage:
$ 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
# This is a comment and will not be part of the presentation
# multiple empty lines between paragraphs are also ignored
# The following lines should produce
# one empty slide
\
\
\@this_line_actually_started_with_a_\.png
\#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.
questions?

BIN
sent/nyan.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 901 B

BIN
sent/sent-1.tar.gz Normal file

Binary file not shown.

View File

@ -0,0 +1,72 @@
From 3a348cc15a97df8e8784b129800293dcfba28f3f Mon Sep 17 00:00:00 2001
From: Sunur Efe Vural <efe@efe.kim>
Date: Wed, 13 Feb 2019 14:28:17 -0500
Subject: [PATCH] Commandline Options
A simple patch that adds extra commandline options to sent.
---
sent.1 | 11 +++++++++++
sent.c | 11 ++++++++++-
2 files changed, 21 insertions(+), 1 deletion(-)
diff --git a/sent.1 b/sent.1
index fabc614..5d55bf4 100644
--- a/sent.1
+++ b/sent.1
@@ -5,6 +5,9 @@
.Nd simple plaintext presentation tool
.Sh SYNOPSIS
.Nm
+.Op Fl f Ar font
+.Op Fl c Ar fgcolor
+.Op Fl b Ar bgcolor
.Op Fl v
.Op Ar file
.Sh DESCRIPTION
@@ -21,6 +24,14 @@ few minutes.
.Bl -tag -width Ds
.It Fl v
Print version information to stdout and exit.
+.It Fl f Ar font
+Defines the
+.Ar font
+when sent is run.
+.It Fl c Ar fgcolor
+Defines the foreground color when sent is run.
+.It Fl b Ar bgcolor
+Defines the background color when sent is run.
.El
.Sh USAGE
.Bl -tag -width Ds
diff --git a/sent.c b/sent.c
index c50a572..0b36e32 100644
--- a/sent.c
+++ b/sent.c
@@ -675,7 +675,7 @@ configure(XEvent *e)
void
usage()
{
- die("usage: %s [file]", argv0);
+ die("usage: %s [-c fgcolor] [-b bgcolor] [-f font] [file]", argv0);
}
int
@@ -687,6 +687,15 @@ main(int argc, char *argv[])
case 'v':
fprintf(stderr, "sent-"VERSION"\n");
return 0;
+ case 'f':
+ fontfallbacks[0] = EARGF(usage());
+ break;
+ case 'c':
+ colors[0] = EARGF(usage());
+ break;
+ case 'b':
+ colors[1] = EARGF(usage());
+ break;
default:
usage();
} ARGEND
--
2.20.1

View File

@ -0,0 +1,31 @@
diff --git a/config.def.h b/config.def.h
index 60eb376..25d89e0 100644
--- a/config.def.h
+++ b/config.def.h
@@ -19,6 +19,9 @@ static const float linespacing = 1.4;
static const float usablewidth = 0.75;
static const float usableheight = 0.75;
+/* height of the presentation progress bar */
+static const int progressheight = 5;
+
static Mousekey mshortcuts[] = {
/* button function argument */
{ Button1, advance, {.i = +1} },
diff --git a/sent.c b/sent.c
index c50a572..046466e 100644
--- a/sent.c
+++ b/sent.c
@@ -533,6 +533,12 @@ xdraw()
0,
slides[idx].lines[i],
0);
+ if (idx != 0 && progressheight != 0) {
+ drw_rect(d,
+ 0, xw.h - progressheight,
+ (xw.w * idx)/(slidecount - 1), progressheight,
+ 1, 0);
+ }
drw_map(d, xw.win, 0, 0, xw.w, xw.h);
} else {
if (!(im->state & SCALED))

79
sent/sent.1 Normal file
View File

@ -0,0 +1,79 @@
.Dd 2016-08-12
.Dt SENT 1
.Sh NAME
.Nm sent
.Nd simple plaintext presentation tool
.Sh SYNOPSIS
.Nm
.Op Fl f Ar font
.Op Fl c Ar fgcolor
.Op Fl b Ar bgcolor
.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.
.It Fl f Ar font
Defines the
.Ar font
when sent is run.
.It Fl c Ar fgcolor
Defines the foreground color when sent is run.
.It Fl b Ar bgcolor
Defines the background color when sent is run.
.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

721
sent/sent.c Normal file
View File

@ -0,0 +1,721 @@
/* See LICENSE file for copyright and license details. */
#include <sys/types.h>
#include <arpa/inet.h>
#include <errno.h>
#include <fcntl.h>
#include <math.h>
#include <regex.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>
#include <X11/Xlib.h>
#include <X11/Xutil.h>
#include <X11/Xft/Xft.h>
#include "arg.h"
#include "util.h"
#include "drw.h"
char *argv0;
/* macros */
#define LEN(a) (sizeof(a) / sizeof(a)[0])
#define LIMIT(x, a, b) (x) = (x) < (a) ? (a) : (x) > (b) ? (b) : (x)
#define MAXFONTSTRLEN 128
typedef enum {
NONE = 0,
SCALED = 1,
} imgstate;
typedef struct {
unsigned char *buf;
unsigned int bufwidth, bufheight;
imgstate state;
XImage *ximg;
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 */
typedef struct {
Display *dpy;
Window win;
Atom wmdeletewin, netwmname;
Visual *vis;
XSetWindowAttributes attrs;
int scr;
int w, h;
int uw, uh; /* usable dimensions for drawing text and images */
} XWindow;
typedef union {
int i;
unsigned int ui;
float f;
const void *v;
} Arg;
typedef struct {
unsigned int b;
void (*func)(const Arg *);
const Arg arg;
} Mousekey;
typedef struct {
KeySym keysym;
void (*func)(const Arg *);
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 void getfontsize(Slide *s, unsigned int *width, unsigned int *height);
static void cleanup(int slidesonly);
static void reload(const Arg *arg);
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();
static void usage();
static void xdraw();
static void xhints();
static void xinit();
static void xloadfonts();
static void bpress(XEvent *);
static void cmessage(XEvent *);
static void expose(XEvent *);
static void kpress(XEvent *);
static void configure(XEvent *);
/* config.h for applying patches and the configuration. */
#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 Fnt *fonts[NUMFONTSCALES];
static int running = 1;
static void (*handler[LASTEvent])(XEvent *) = {
[ButtonPress] = bpress,
[ClientMessage] = cmessage,
[ConfigureNotify] = configure,
[Expose] = expose,
[KeyPress] = kpress,
};
int
filter(int fd, const char *cmd)
{
int fds[2];
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);
}
close(fds[1]);
return fds[0];
}
void
fffree(Image *img)
{
free(img->buf);
if (img->ximg)
XDestroyImage(img->ximg);
free(img);
}
void
ffload(Slide *s)
{
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;
if (s->img || !(filename = s->embed) || !s->embed[0])
return; /* already done */
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 ((fdin = open(filename, O_RDONLY)) < 0)
die("sent: Unable to open '%s':", filename);
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]);
if (s->img->buf)
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);
}
void
ffprepare(Image *img)
{
int depth = DefaultDepth(xw.dpy, xw.scr);
int width = xw.uw;
int height = xw.uh;
if (xw.uw * img->bufheight > xw.uh * img->bufwidth)
width = img->bufwidth * xw.uh / img->bufheight;
else
height = img->bufheight * xw.uw / img->bufwidth;
if (depth < 24)
die("sent: Display color depths < 24 not supported");
if (!(img->ximg = XCreateImage(xw.dpy, CopyFromParent, depth, ZPixmap, 0,
NULL, width, height, 32, 0)))
die("sent: Unable to create XImage");
img->ximg->data = ecalloc(height, img->ximg->bytes_per_line);
if (!XInitImage(img->ximg))
die("sent: Unable to initiate XImage");
ffscale(img);
img->state |= SCALED;
}
void
ffscale(Image *img)
{
unsigned int x, y;
unsigned int width = img->ximg->width;
unsigned int height = img->ximg->height;
char* newBuf = img->ximg->data;
unsigned char* ibuf;
unsigned int jdy = img->ximg->bytes_per_line / 4 - width;
unsigned int dx = (img->bufwidth << 10) / width;
for (y = 0; y < height; y++) {
unsigned int bufx = img->bufwidth / width;
ibuf = &img->buf[y * img->bufheight / height * img->bufwidth * 3];
for (x = 0; x < width; x++) {
*newBuf++ = (ibuf[(bufx >> 10)*3+2]);
*newBuf++ = (ibuf[(bufx >> 10)*3+1]);
*newBuf++ = (ibuf[(bufx >> 10)*3+0]);
newBuf++;
bufx += dx;
}
newBuf += jdy;
}
}
void
ffdraw(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);
}
void
getfontsize(Slide *s, unsigned int *width, unsigned int *height)
{
int i, j;
unsigned int curw, newmax;
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) {
drw_setfontset(d, fonts[--j]);
curw = drw_fontset_getwidth(d, s->lines[i]);
}
if (newmax)
*width = curw;
}
*height = fonts[j]->h * lfac;
}
void
cleanup(int slidesonly)
{
unsigned int i, j;
if (!slidesonly) {
for (i = 0; i < NUMFONTSCALES; i++)
drw_fontset_free(fonts[i]);
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);
}
if (!slidesonly) {
free(slides);
slides = NULL;
}
}
}
void
reload(const Arg *arg)
{
FILE *fp = NULL;
unsigned int i;
if (!fname) {
fprintf(stderr, "sent: Cannot reload from stdin. Use a file!\n");
return;
}
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
load(FILE *fp)
{
static size_t size = 0;
size_t blen, maxlines;
char buf[BUFSIZ], *p;
Slide *s;
/* read each line from fp and add it to the item list */
while (1) {
/* eat consecutive empty lines */
while ((p = fgets(buf, sizeof(buf), fp)))
if (strcmp(buf, "\n") != 0 && buf[0] != '#')
break;
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 (buf[0] == '#')
continue;
/* grow lines array */
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]));
}
blen = strlen(buf);
if (!(s->lines[s->linecount] = strdup(buf)))
die("sent: Unable to 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];
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;
}
}
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;
idx = new_idx;
xdraw();
}
}
void
quit(const Arg *arg)
{
running = 0;
}
void
resize(int width, int height)
{
xw.w = width;
xw.h = height;
xw.uw = usablewidth * width;
xw.uh = usableheight * height;
drw_resize(d, width, height);
}
void
run()
{
XEvent ev;
/* Waiting for window mapping */
while (1) {
XNextEvent(xw.dpy, &ev);
if (ev.type == ConfigureNotify) {
resize(ev.xconfigure.width, ev.xconfigure.height);
} else if (ev.type == MapNotify) {
break;
}
}
while (running) {
XNextEvent(xw.dpy, &ev);
if (handler[ev.type])
(handler[ev.type])(&ev);
}
}
void
xdraw()
{
unsigned int height, width, i;
Image *im = slides[idx].img;
getfontsize(&slides[idx], &width, &height);
XClearWindow(xw.dpy, xw.win);
if (!im) {
drw_rect(d, 0, 0, xw.w, xw.h, 1, 1);
for (i = 0; i < slides[idx].linecount; i++)
drw_text(d,
(xw.w - width) / 2,
(xw.h - height) / 2 + i * linespacing * d->fonts->h,
width,
d->fonts->h,
0,
slides[idx].lines[i],
0);
if (idx != 0 && progressheight != 0) {
drw_rect(d,
0, xw.h - progressheight,
(xw.w * idx)/(slidecount - 1), progressheight,
1, 0);
}
drw_map(d, xw.win, 0, 0, xw.w, xw.h);
} else {
if (!(im->state & SCALED))
ffprepare(im);
ffdraw(im);
}
}
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");
sizeh->flags = PSize;
sizeh->height = xw.h;
sizeh->width = xw.w;
XSetWMProperties(xw.dpy, xw.win, NULL, NULL, NULL, 0, sizeh, &wm, &class);
XFree(sizeh);
}
void
xinit()
{
XTextProperty prop;
unsigned int i;
if (!(xw.dpy = XOpenDisplay(NULL)))
die("sent: Unable to 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.bit_gravity = CenterGravity;
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.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);
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);
XSetTextProperty(xw.dpy, xw.win, &prop, xw.netwmname);
XFree(prop.value);
XMapWindow(xw.dpy, xw.win);
xhints();
XSync(xw.dpy, False);
}
void
xloadfonts()
{
int i, j;
char *fstrs[LEN(fontfallbacks)];
for (j = 0; j < LEN(fontfallbacks); j++) {
fstrs[j] = ecalloc(1, MAXFONTSTRLEN);
}
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");
}
if (!(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++)
if (fstrs[j])
free(fstrs[j]);
}
void
bpress(XEvent *e)
{
unsigned int i;
for (i = 0; i < LEN(mshortcuts); i++)
if (e->xbutton.button == mshortcuts[i].b && mshortcuts[i].func)
mshortcuts[i].func(&(mshortcuts[i].arg));
}
void
cmessage(XEvent *e)
{
if (e->xclient.data.l[0] == xw.wmdeletewin)
running = 0;
}
void
expose(XEvent *e)
{
if (0 == e->xexpose.count)
xdraw();
}
void
kpress(XEvent *e)
{
unsigned int i;
KeySym sym;
sym = XkbKeycodeToKeysym(xw.dpy, (KeyCode)e->xkey.keycode, 0, 0);
for (i = 0; i < LEN(shortcuts); i++)
if (sym == shortcuts[i].keysym && shortcuts[i].func)
shortcuts[i].func(&(shortcuts[i].arg));
}
void
configure(XEvent *e)
{
resize(e->xconfigure.width, e->xconfigure.height);
if (slides[idx].img)
slides[idx].img->state &= ~SCALED;
xdraw();
}
void
usage()
{
die("usage: %s [-c fgcolor] [-b bgcolor] [-f font] [file]", argv0);
}
int
main(int argc, char *argv[])
{
FILE *fp = NULL;
ARGBEGIN {
case 'v':
fprintf(stderr, "sent-"VERSION"\n");
return 0;
case 'f':
fontfallbacks[0] = EARGF(usage());
break;
case 'c':
colors[0] = EARGF(usage());
break;
case 'b':
colors[1] = EARGF(usage());
break;
default:
usage();
} 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);
load(fp);
fclose(fp);
if (!slidecount)
usage();
xinit();
run();
cleanup(0);
return 0;
}

715
sent/sent.c.orig Normal file
View File

@ -0,0 +1,715 @@
/* See LICENSE file for copyright and license details. */
#include <sys/types.h>
#include <arpa/inet.h>
#include <errno.h>
#include <fcntl.h>
#include <math.h>
#include <regex.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>
#include <X11/Xlib.h>
#include <X11/Xutil.h>
#include <X11/Xft/Xft.h>
#include "arg.h"
#include "util.h"
#include "drw.h"
char *argv0;
/* macros */
#define LEN(a) (sizeof(a) / sizeof(a)[0])
#define LIMIT(x, a, b) (x) = (x) < (a) ? (a) : (x) > (b) ? (b) : (x)
#define MAXFONTSTRLEN 128
typedef enum {
NONE = 0,
SCALED = 1,
} imgstate;
typedef struct {
unsigned char *buf;
unsigned int bufwidth, bufheight;
imgstate state;
XImage *ximg;
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 */
typedef struct {
Display *dpy;
Window win;
Atom wmdeletewin, netwmname;
Visual *vis;
XSetWindowAttributes attrs;
int scr;
int w, h;
int uw, uh; /* usable dimensions for drawing text and images */
} XWindow;
typedef union {
int i;
unsigned int ui;
float f;
const void *v;
} Arg;
typedef struct {
unsigned int b;
void (*func)(const Arg *);
const Arg arg;
} Mousekey;
typedef struct {
KeySym keysym;
void (*func)(const Arg *);
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 void getfontsize(Slide *s, unsigned int *width, unsigned int *height);
static void cleanup(int slidesonly);
static void reload(const Arg *arg);
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();
static void usage();
static void xdraw();
static void xhints();
static void xinit();
static void xloadfonts();
static void bpress(XEvent *);
static void cmessage(XEvent *);
static void expose(XEvent *);
static void kpress(XEvent *);
static void configure(XEvent *);
/* config.h for applying patches and the configuration. */
#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 Fnt *fonts[NUMFONTSCALES];
static int running = 1;
static void (*handler[LASTEvent])(XEvent *) = {
[ButtonPress] = bpress,
[ClientMessage] = cmessage,
[ConfigureNotify] = configure,
[Expose] = expose,
[KeyPress] = kpress,
};
int
filter(int fd, const char *cmd)
{
int fds[2];
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);
}
close(fds[1]);
return fds[0];
}
void
fffree(Image *img)
{
free(img->buf);
if (img->ximg)
XDestroyImage(img->ximg);
free(img);
}
void
ffload(Slide *s)
{
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;
if (s->img || !(filename = s->embed) || !s->embed[0])
return; /* already done */
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 ((fdin = open(filename, O_RDONLY)) < 0)
die("sent: Unable to open '%s':", filename);
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]);
if (s->img->buf)
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);
}
void
ffprepare(Image *img)
{
int depth = DefaultDepth(xw.dpy, xw.scr);
int width = xw.uw;
int height = xw.uh;
if (xw.uw * img->bufheight > xw.uh * img->bufwidth)
width = img->bufwidth * xw.uh / img->bufheight;
else
height = img->bufheight * xw.uw / img->bufwidth;
if (depth < 24)
die("sent: Display color depths < 24 not supported");
if (!(img->ximg = XCreateImage(xw.dpy, CopyFromParent, depth, ZPixmap, 0,
NULL, width, height, 32, 0)))
die("sent: Unable to create XImage");
img->ximg->data = ecalloc(height, img->ximg->bytes_per_line);
if (!XInitImage(img->ximg))
die("sent: Unable to initiate XImage");
ffscale(img);
img->state |= SCALED;
}
void
ffscale(Image *img)
{
unsigned int x, y;
unsigned int width = img->ximg->width;
unsigned int height = img->ximg->height;
char* newBuf = img->ximg->data;
unsigned char* ibuf;
unsigned int jdy = img->ximg->bytes_per_line / 4 - width;
unsigned int dx = (img->bufwidth << 10) / width;
for (y = 0; y < height; y++) {
unsigned int bufx = img->bufwidth / width;
ibuf = &img->buf[y * img->bufheight / height * img->bufwidth * 3];
for (x = 0; x < width; x++) {
*newBuf++ = (ibuf[(bufx >> 10)*3+2]);
*newBuf++ = (ibuf[(bufx >> 10)*3+1]);
*newBuf++ = (ibuf[(bufx >> 10)*3+0]);
newBuf++;
bufx += dx;
}
newBuf += jdy;
}
}
void
ffdraw(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);
}
void
getfontsize(Slide *s, unsigned int *width, unsigned int *height)
{
int i, j;
unsigned int curw, newmax;
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) {
drw_setfontset(d, fonts[--j]);
curw = drw_fontset_getwidth(d, s->lines[i]);
}
if (newmax)
*width = curw;
}
*height = fonts[j]->h * lfac;
}
void
cleanup(int slidesonly)
{
unsigned int i, j;
if (!slidesonly) {
for (i = 0; i < NUMFONTSCALES; i++)
drw_fontset_free(fonts[i]);
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);
}
if (!slidesonly) {
free(slides);
slides = NULL;
}
}
}
void
reload(const Arg *arg)
{
FILE *fp = NULL;
unsigned int i;
if (!fname) {
fprintf(stderr, "sent: Cannot reload from stdin. Use a file!\n");
return;
}
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
load(FILE *fp)
{
static size_t size = 0;
size_t blen, maxlines;
char buf[BUFSIZ], *p;
Slide *s;
/* read each line from fp and add it to the item list */
while (1) {
/* eat consecutive empty lines */
while ((p = fgets(buf, sizeof(buf), fp)))
if (strcmp(buf, "\n") != 0 && buf[0] != '#')
break;
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 (buf[0] == '#')
continue;
/* grow lines array */
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]));
}
blen = strlen(buf);
if (!(s->lines[s->linecount] = strdup(buf)))
die("sent: Unable to 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];
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;
}
}
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;
idx = new_idx;
xdraw();
}
}
void
quit(const Arg *arg)
{
running = 0;
}
void
resize(int width, int height)
{
xw.w = width;
xw.h = height;
xw.uw = usablewidth * width;
xw.uh = usableheight * height;
drw_resize(d, width, height);
}
void
run()
{
XEvent ev;
/* Waiting for window mapping */
while (1) {
XNextEvent(xw.dpy, &ev);
if (ev.type == ConfigureNotify) {
resize(ev.xconfigure.width, ev.xconfigure.height);
} else if (ev.type == MapNotify) {
break;
}
}
while (running) {
XNextEvent(xw.dpy, &ev);
if (handler[ev.type])
(handler[ev.type])(&ev);
}
}
void
xdraw()
{
unsigned int height, width, i;
Image *im = slides[idx].img;
getfontsize(&slides[idx], &width, &height);
XClearWindow(xw.dpy, xw.win);
if (!im) {
drw_rect(d, 0, 0, xw.w, xw.h, 1, 1);
for (i = 0; i < slides[idx].linecount; i++)
drw_text(d,
(xw.w - width) / 2,
(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);
}
}
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");
sizeh->flags = PSize;
sizeh->height = xw.h;
sizeh->width = xw.w;
XSetWMProperties(xw.dpy, xw.win, NULL, NULL, NULL, 0, sizeh, &wm, &class);
XFree(sizeh);
}
void
xinit()
{
XTextProperty prop;
unsigned int i;
if (!(xw.dpy = XOpenDisplay(NULL)))
die("sent: Unable to 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.bit_gravity = CenterGravity;
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.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);
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);
XSetTextProperty(xw.dpy, xw.win, &prop, xw.netwmname);
XFree(prop.value);
XMapWindow(xw.dpy, xw.win);
xhints();
XSync(xw.dpy, False);
}
void
xloadfonts()
{
int i, j;
char *fstrs[LEN(fontfallbacks)];
for (j = 0; j < LEN(fontfallbacks); j++) {
fstrs[j] = ecalloc(1, MAXFONTSTRLEN);
}
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");
}
if (!(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++)
if (fstrs[j])
free(fstrs[j]);
}
void
bpress(XEvent *e)
{
unsigned int i;
for (i = 0; i < LEN(mshortcuts); i++)
if (e->xbutton.button == mshortcuts[i].b && mshortcuts[i].func)
mshortcuts[i].func(&(mshortcuts[i].arg));
}
void
cmessage(XEvent *e)
{
if (e->xclient.data.l[0] == xw.wmdeletewin)
running = 0;
}
void
expose(XEvent *e)
{
if (0 == e->xexpose.count)
xdraw();
}
void
kpress(XEvent *e)
{
unsigned int i;
KeySym sym;
sym = XkbKeycodeToKeysym(xw.dpy, (KeyCode)e->xkey.keycode, 0, 0);
for (i = 0; i < LEN(shortcuts); i++)
if (sym == shortcuts[i].keysym && shortcuts[i].func)
shortcuts[i].func(&(shortcuts[i].arg));
}
void
configure(XEvent *e)
{
resize(e->xconfigure.width, e->xconfigure.height);
if (slides[idx].img)
slides[idx].img->state &= ~SCALED;
xdraw();
}
void
usage()
{
die("usage: %s [-c fgcolor] [-b bgcolor] [-f font] [file]", argv0);
}
int
main(int argc, char *argv[])
{
FILE *fp = NULL;
ARGBEGIN {
case 'v':
fprintf(stderr, "sent-"VERSION"\n");
return 0;
case 'f':
fontfallbacks[0] = EARGF(usage());
break;
case 'c':
colors[0] = EARGF(usage());
break;
case 'b':
colors[1] = EARGF(usage());
break;
default:
usage();
} 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);
load(fp);
fclose(fp);
if (!slidecount)
usage();
xinit();
run();
cleanup(0);
return 0;
}

BIN
sent/transparent_test.ff Normal file

Binary file not shown.

35
sent/util.c Normal file
View File

@ -0,0 +1,35 @@
/* See LICENSE file for copyright and license details. */
#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, ...) {
va_list ap;
va_start(ap, fmt);
vfprintf(stderr, fmt, ap);
va_end(ap);
if (fmt[0] && fmt[strlen(fmt)-1] == ':') {
fputc(' ', stderr);
perror(NULL);
} else {
fputc('\n', stderr);
}
exit(1);
}

8
sent/util.h Normal file
View File

@ -0,0 +1,8 @@
/* See LICENSE file for copyright and license details. */
#define MAX(A, B) ((A) > (B) ? (A) : (B))
#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);